---
title: Idempotency
source: https://se-hari.com/docs/idempotency
updated: 2026-08-16T01:05:07.97437+00:00
---

> Cara mengulang request yang gagal tanpa risiko membuat meeting ganda atau membayar dua kali untuk pekerjaan yang sama.

Jaringan gagal di tempat yang paling merepotkan: setelah request Anda sampai dan dikerjakan, tapi sebelum responsnya kembali. Dari sisi Anda, kedua kemungkinan itu terlihat sama persis — timeout. Mengulang berarti berisiko mengerjakan hal yang sama dua kali; tidak mengulang berarti berisiko kehilangan pekerjaan.

`Idempotency-Key` menyelesaikan itu.

## Cara kerjanya

Kirim header `Idempotency-Key` berisi nilai unik pada request yang mengubah keadaan:

```bash
curl -X POST "https://se-hari.com/api/v1/meetings" \
  -H "Authorization: Bearer $SEHARI_API_KEY" \
  -H "Idempotency-Key: 7f3a9c21-4b8e-4d6f-a1c2-9e8b7a6d5c4f" \
  -H "Content-Type: application/json" \
  -d '{"title":"Rapat mingguan","date":"2026-09-01","start_time":"09:00","pricing_tier_id":"..."}'
```

Kalau request dengan key yang sama datang lagi, kami **tidak mengerjakannya lagi**. Kami mengembalikan respons yang pertama, dengan status yang sama seperti aslinya. Meeting hanya terbuat sekali, kredit hanya terpotong sekali.

Key disimpan 24 jam. Setelah itu key yang sama dianggap baru.

## Membangun key yang benar

Kuncinya: key harus berkorespondensi satu-satu dengan **pekerjaan**, bukan dengan waktu.

```javascript
// BENAR — satu key per item pekerjaan
const key = `arsip-harian-${tanggal}-${recording.id}`;

// BENAR — di n8n, mengikuti identitas eksekusi
// {{$workflow.id}}-{{$execution.id}}-{{$itemIndex}}

// SALAH — key konstan untuk isi yang berubah-ubah
const key = 'arsip-harian';

// SALAH — presisi kasar, dua pekerjaan berbeda bisa bertabrakan
const key = `arsip-${Math.floor(Date.now() / 60000)}`;
```

Yang penting, key harus **tetap sama saat Anda mengulang**. Membangkitkan UUID baru di setiap percobaan menghilangkan seluruh manfaatnya — dan itulah cara paling umum menciptakan meeting ganda yang kreditnya terpotong dua kali.

```javascript
// Key dibuat SEKALI, di luar perulangan retry.
const key = crypto.randomUUID();

for (let percobaan = 0; percobaan < 3; percobaan++) {
	const res = await fetch('https://se-hari.com/api/v1/meetings', {
		method: 'POST',
		headers: {
			Authorization: `Bearer ${process.env.SEHARI_API_KEY}`,
			'Idempotency-Key': key,
			'Content-Type': 'application/json'
		},
		body: JSON.stringify(payload)
	});

	if (res.status < 500) return res;
	await new Promise((r) => setTimeout(r, 2 ** percobaan * 1000));
}
```

## Key yang sama, body yang berbeda

Kalau Anda mengirim key yang sudah dipakai tapi dengan body yang berbeda, kami menolaknya dengan [`idempotency_key_reused`](/docs/error/idempotency-key-reused) — bukan mengembalikan hasil yang lama.

Ini melindungi Anda dari kesalahan yang tanpanya tidak akan pernah Anda sadari. Kalau kami diam-diam mengembalikan hasil yang pertama, workflow Anda akan menganggap item kedua sudah dikerjakan padahal tidak, dan hasilnya adalah pekerjaan yang hilang tanpa satu pun error.

## Kapan wajib dipakai

Idempotency paling penting justru pada kegagalan yang paling membingungkan:

- **`500 internal_error`** — request mungkin sudah sebagian dikerjakan. Ini kasus utamanya.
- **`502 upstream_error`** — Zoom mungkin sudah membuat meeting sebelum langkah kami gagal.
- **Timeout jaringan** — Anda tidak tahu apa-apa tentang apa yang terjadi di sisi kami.

Untuk `400` dan `403`, idempotency tidak diperlukan: tidak ada yang sempat dikerjakan, jadi tidak ada yang bisa terduplikasi.

## Operasi yang mendukungnya

Semua endpoint yang mengubah keadaan menerima `Idempotency-Key`. Yang paling penting adalah dua yang menyentuh uang:

- `POST /meetings` — memotong kredit saat itu juga
- `POST /notes` — mengantre pekerjaan yang nanti ditagih

Endpoint `GET` tidak membutuhkannya. Ia sudah idempoten menurut definisi HTTP: memanggilnya sepuluh kali sama saja dengan sekali.

## Kalau request pertama masih berjalan

Ada satu kasus yang layak diketahui: dua request dengan `Idempotency-Key` yang sama dikirim nyaris bersamaan, dan yang pertama belum selesai saat yang kedua tiba.

Request kedua **ditolak `409 conflict`** dengan pesan bahwa key tersebut sedang diproses. Ia tidak menunggu, dan tidak mengerjakan apa pun — yang penting, ia juga tidak menghasilkan pekerjaan kedua.

```json
{
  "error": {
    "code": "conflict",
    "message": "Request dengan Idempotency-Key ini sedang diproses. Tunggu sampai selesai, lalu ambil hasilnya lewat endpoint baca."
  }
}
```

Menahan request kedua sampai yang pertama selesai akan terasa lebih ramah, tapi artinya koneksi Anda menggantung selama pekerjaan berjalan — dan pembuatan meeting Zoom bisa memakan belasan detik. Menolak cepat dengan pesan yang jelas lebih mudah ditangani daripada timeout yang tidak menjelaskan apa-apa.

Yang perlu dilakukan saat menemuinya: tunggu sebentar, lalu **ambil hasilnya lewat endpoint baca** alih-alih mengirim ulang. Pekerjaannya sedang dikerjakan; mengirim ulang tidak mempercepatnya.

Perlu dibedakan dari kasus setelah selesai: begitu request pertama **selesai**, key yang sama mengembalikan hasilnya seketika — itulah putar-ulang yang sesungguhnya. Dan kalau request pertama **gagal**, key-nya dibebaskan sehingga percobaan berikutnya benar-benar dikerjakan; kegagalan tidak pernah dicatat sebagai "sudah pernah dilakukan".

Ada satu kasus ketiga yang jarang tapi nyata: proses yang mengerjakan request pertama **mati di tengah jalan** — server di-restart saat deploy, misalnya. Tidak ada yang sempat menandai selesai maupun gagal. Kalau key itu dibiarkan menggantung, ia akan menolak setiap percobaan ulang selama 24 jam berikutnya, padahal tidak ada apa pun yang sedang berjalan.

Karena itu key yang berstatus "sedang diproses" lebih dari lima menit dianggap ditinggalkan, dan percobaan berikutnya dengan isi yang sama mengambil alih tempatnya. Lima menit jauh di atas operasi terlama kami, jadi request yang benar-benar masih berjalan tidak akan pernah terganggu. Isi yang berbeda tetap ditolak — pengambilalihan hanya sah untuk pengulangan request yang sama.

Konsekuensinya untuk sistem dengan beberapa worker: kalau dua worker tanpa sengaja mengambil pekerjaan yang sama, salah satunya mendapat 409 dan berhenti — bukan menghasilkan meeting kedua. Itu hasil yang diinginkan, meski bentuknya sebuah error.

## Perlindungan bawaan lain

Beberapa operasi punya pengaman tambahan yang tidak bergantung pada header ini. Satu rekaman hanya boleh punya satu notulen, jadi `POST /notes` untuk rekaman yang sudah diproses dijawab [`409 conflict`](/docs/error/conflict) beserta id notulen yang sudah ada — bahkan tanpa `Idempotency-Key` sama sekali.

Anggap itu jaring pengaman, bukan pengganti. Ia melindungi dari duplikasi jenis tertentu, sementara `Idempotency-Key` melindungi dari duplikasi jenis apa pun.

## Selanjutnya

- [`idempotency_key_reused`](/docs/error/idempotency-key-reused) — kalau key ditolak
- [Rate limit](/docs/rate-limit) — pola retry yang tidak memperburuk keadaan
- [n8n](/docs/n8n) — node Se-Hari mengirim key ini otomatis