---
title: POST /meetings
source: https://se-hari.com/docs/api/api-meetings-create
updated: 2026-08-15T18:52:41.470707+00:00
---

> Buat meeting Zoom. POST /meetings — butuh scope meetings:write.

Membuat meeting Zoom berlisensi atas nama pemilik API key dan LANGSUNG memotong kredit sesuai paket harga yang dipilih. Berbeda dari notulen, biaya di sini pasti dan terjadi seketika. Tanggal dan jam diinterpretasikan sebagai WIB (UTC+7), bukan UTC — kirim `date` sebagai YYYY-MM-DD dan `start_time` sebagai HH:mm dalam waktu setempat Indonesia. Nilai `pricing_tier_id` harus berasal dari GET /pricing-tiers. Kalau `auto_notulen` diaktifkan, sistem MEMAKSA perekaman menyala apa pun nilai `with_recording` yang Anda kirim, karena meeting tanpa rekaman tidak akan pernah menghasilkan notulen. Respons selalu menyebutkan nilai `with_recording` hasil koersi itu, sehingga Anda tahu kalau biayanya bertambah. Kalau tidak ada host Zoom yang tersedia pada rentang waktu yang diminta, endpoint menjawab 503 dengan Retry-After — itu kondisi sementara, bukan kesalahan request.
## Endpoint
```bash
POST https://se-hari.com/api/v1/meetings
```
Butuh scope `meetings: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 |
|---|---|---|---|
| `title` | `string` | ya | min 1 karakter, maks 200 karakter |
| `date` | `string` | ya | Tanggal dalam zona WIB, format YYYY-MM-DD |
| `start_time` | `string` | ya | Jam dalam zona WIB, format HH:mm |
| `pricing_tier_id` | `string` | ya | min 1 karakter |
| `description` | `string` | tidak | maks 1000 karakter |
| `password` | `string` | tidak | min 4 karakter, maks 10 karakter |
| `with_recording` | `boolean` | tidak | — |
| `auto_notulen` | `boolean` | tidak | — |
| `must_login` | `boolean` | tidak | — |
| `mic_off` | `boolean` | tidak | — |
| `email_notification` | `boolean` | tidak | — |

## Contoh
```bash
curl -X POST "https://se-hari.com/api/v1/meetings" \
  -H "Authorization: Bearer $SEHARI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"Demo Klien","date":"2026-08-16","start_time":"10:00","pricing_tier_id":"pt_100_jam","auto_notulen":true}'
```
```javascript
const res = await fetch('https://se-hari.com/api/v1/meetings', {
	method: 'POST',
		headers: {
				'Authorization': `Bearer ${process.env.SEHARI_API_KEY}`,
				'Content-Type': 'application/json',
			},
	body: JSON.stringify({
	 "title": "Demo Klien",
	 "date": "2026-08-16",
	 "start_time": "10:00",
	 "pricing_tier_id": "pt_100_jam",
	 "auto_notulen": true
	})
});

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/meetings",
    headers={"Authorization": f"Bearer {os.environ['SEHARI_API_KEY']}"},
    json={"title":"Demo Klien","date":"2026-08-16","start_time":"10:00","pricing_tier_id":"pt_100_jam","auto_notulen":True},
    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 |
|---|---|---|
| `id` | `string` | — |
| `title` | `string` | — |
| `status` | `string` | — |
| `start_at` | `string` | — |
| `start_at_wib` | `string` | — |
| `end_at` | `string` \| `null` | — |
| `duration` | `number` \| `null` | — |
| `duration_unit` | `string` \| `null` | — |
| `capacity` | `number` \| `null` | — |
| `join_url` | `string` \| `null` | — |
| `password` | `string` \| `null` | — |
| `host_key` | `string` \| `null` | — |
| `zoom_meeting_id` | `string` \| `null` | — |
| `auto_notulen` | `boolean` | — |
| `created_at` | `string` | — |
| `with_recording` | `boolean` | — |
| `credits_deducted` | `integer` \| `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_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`](/docs/error/invalid-request) | Body tidak lolos validasi. Field yang bermasalah disebut di `details`. |
| 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. |
| 422 | [`unprocessable_entity`](/docs/error/unprocessable-entity) | Bentuk request benar, tapi isinya tidak bisa diproses. |
| 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. |
| 502 | [`upstream_error`](/docs/error/upstream-error) | Layanan pihak ketiga (Zoom, penyedia AI) menolak permintaan. |
| 503 | [`service_unavailable`](/docs/error/service-unavailable) | Layanan sedang tidak tersedia sementara. |
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

Meeting dibuat dari sebuah paket harga, bukan dari durasi dan kapasitas lepas. Paket menentukan berapa peserta yang muat, berapa lama meeting boleh berjalan, dan berapa kredit yang dipotong — jadi langkah pertama selalu melihat katalognya.

1. `GET /pricing-tiers` — lihat paket yang tersedia beserta biayanya
2. `POST /credits/estimate` dengan `type: "meeting"` — pastikan saldo cukup sebelum memotong apa pun
3. `POST /meetings` — buat meetingnya; kredit terpotong saat ini juga
4. Bagikan `join_url` dari respons ke peserta

Membatalkan meeting yang belum dimulai mengembalikan kreditnya. Yang sudah berjalan tidak — waktu host Zoom sudah terpakai dan tidak bisa ditarik kembali. Karena itu `DELETE /meetings/{id}` menjawab berbeda tergantung status, dan bukan karena kesalahan Anda.

## 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.
- **Hormati `Retry-After`.** Mencoba lagi lebih cepat dari yang disebutkan hanya memperpanjang masa tunggu. Kalau Anda sering menyentuhnya, kemungkinan besar Anda sedang polling sesuatu yang seharusnya ditangani webhook.

## 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