Kontrak API untuk agen AI tidak cukup hanya “bisa dipanggil”. Dalam workflow agentic, agen dapat mengulang request karena timeout, mengirim tool call yang sama beberapa kali, menerima webhook terlambat, atau memproses callback yang datang tidak berurutan. Jika API tidak dirancang untuk kondisi ini, hasilnya bukan sekadar error, tetapi duplicate side effect: transaksi dobel, tiket dobel, email terkirim berulang, atau status sistem menjadi saling bertentangan.
Solusi praktisnya adalah merancang API dengan asumsi bahwa retry akan terjadi, respons bisa terlambat, event bisa duplikat, dan callback bisa balapan satu sama lain. Artikel ini membahas pola desain yang aman: endpoint idempoten, idempotency key, status asynchronous, kebijakan retry, deduplikasi event, kontrak error yang dapat dibaca agen, serta observability minimum agar masalah mudah dilacak.
Mengapa agen AI membuat integrasi API lebih mudah rusak
Pada integrasi tradisional, klien biasanya ditulis cukup deterministik: alur relatif tetap, jumlah request terkendali, dan retry dikelola terbatas. Pada agen AI, pola ini berubah. Agen dapat memutuskan sendiri kapan memanggil tool, mengulang ketika tidak yakin, atau menjalankan beberapa langkah secara paralel. Dalam praktiknya, ini menimbulkan beberapa kelas masalah:
- Timeout semu: server sebenarnya memproses request, tetapi klien tidak menerima respons tepat waktu lalu mengirim ulang.
- Retry tanpa konteks: agen atau middleware melakukan retry otomatis terhadap operasi yang sebenarnya tidak aman untuk diulang.
- Duplicate side effect: pembuatan resource, pembayaran, pengiriman notifikasi, atau enqueue job terjadi lebih dari sekali.
- Webhook terlambat atau dobel: provider mengirim callback beberapa kali atau dalam urutan yang tidak konsisten.
- Race condition antar callback: dua event memutakhirkan entitas yang sama hampir bersamaan dan menghasilkan state salah.
Dalam konteks workflow agentic, anggap semua hal di atas sebagai kondisi normal, bukan edge case.
Prinsip dasar kontrak API yang aman untuk agen AI
1. Pisahkan operasi baca dari operasi yang punya side effect
Operasi baca relatif aman untuk diulang. Operasi tulis tidak. Karena itu, bedakan dengan jelas endpoint yang hanya membaca data dan endpoint yang memicu perubahan state, misalnya membuat order, menjadwalkan eksekusi, atau mengirim email.
Untuk endpoint tulis, hindari kontrak yang mengandalkan asumsi “request ini hanya dikirim sekali”. Anggap klien dapat mengirim request yang sama beberapa kali karena timeout atau retry.
2. Gunakan idempotensi untuk operasi create/execute
Idempotency berarti pengulangan request yang sama tidak menghasilkan side effect tambahan. Ini sangat penting pada tool call agen AI. Jika agen mengirim perintah create payment dua kali karena tidak menerima respons pertama, server harus mengembalikan hasil yang sama, bukan membuat dua pembayaran.
Pola paling umum adalah mewajibkan klien mengirim Idempotency-Key yang unik untuk satu niat operasi. Server menyimpan hasil pemrosesan berdasarkan key tersebut, lalu mengembalikan respons yang sama jika request identik datang lagi.
3. Prioritaskan model asynchronous untuk pekerjaan lambat
Jika operasi bisa memakan waktu lama atau bergantung pada sistem eksternal, jangan memaksakan respons sinkron panjang. Lebih aman mengembalikan 202 Accepted beserta job id atau operation id, lalu sediakan endpoint status yang bisa dipolling agen.
Dengan model ini, timeout jaringan tidak mudah diterjemahkan sebagai “operasi gagal”. Agen cukup menanyakan status operasi yang sudah dibuat.
4. Error harus bisa dipahami mesin, bukan hanya manusia
Banyak API mengembalikan pesan error bebas seperti “something went wrong”. Ini buruk untuk otomasi agen. Kontrak error yang baik perlu memiliki:
- code yang stabil dan dapat dipakai untuk bercabang logika,
- message yang manusiawi untuk debugging,
- retryable untuk memberi sinyal apakah aman dicoba lagi,
- details untuk field validasi atau konteks tambahan.
Desain endpoint idempoten yang realistis
Kapan perlu idempotency key
Gunakan idempotency key pada endpoint yang memenuhi salah satu kondisi berikut:
- Membuat resource baru yang dapat terduplikasi.
- Menjalankan aksi bisnis yang punya side effect eksternal.
- Mengantrekan job yang tidak boleh ganda.
- Memicu integrasi ke provider lain yang tidak mudah dibatalkan.
Contohnya: POST /payments, POST /tickets, POST /dispatch, POST /tool-executions.
Header dan semantik minimal
Kontrak yang sederhana dan cukup kuat biasanya memakai header seperti ini:
POST /v1/tool-executions HTTP/1.1
Idempotency-Key: 7f1f5bb2-3f2f-4a2f-a8a1-8d2f2d9c4a01
Content-Type: application/json
{
"tool": "create_ticket",
"input": {
"customer_id": "cus_123",
"subject": "Pembayaran gagal",
"priority": "high"
}
}Server kemudian perlu melakukan beberapa hal:
- Mencari apakah Idempotency-Key sudah pernah dipakai.
- Jika belum ada, simpan request fingerprint dan mulai proses.
- Jika sudah ada dan payload identik, kembalikan hasil lama atau status operasi yang sama.
- Jika sudah ada tetapi payload berbeda, tolak sebagai konflik karena key yang sama dipakai untuk niat operasi berbeda.
Contoh respons pertama dan respons ulang
HTTP/1.1 202 Accepted
Content-Type: application/json
{
"operation_id": "op_9d13",
"status": "pending",
"status_url": "/v1/operations/op_9d13"
}Jika request yang sama dikirim ulang karena timeout, respons dapat tetap sama:
HTTP/1.1 202 Accepted
Content-Type: application/json
{
"operation_id": "op_9d13",
"status": "pending",
"status_url": "/v1/operations/op_9d13"
}Jika key yang sama dipakai dengan payload berbeda:
HTTP/1.1 409 Conflict
Content-Type: application/json
{
"error": {
"code": "IDEMPOTENCY_KEY_REUSED_WITH_DIFFERENT_PAYLOAD",
"message": "Idempotency-Key sudah dipakai untuk request yang berbeda.",
"retryable": false
}
}Fingerprint request dan masa simpan key
Server sebaiknya menyimpan fingerprint dari request yang relevan, misalnya hash dari method, path, dan payload yang telah dinormalisasi. Tujuannya untuk mendeteksi penyalahgunaan key yang sama.
Masa simpan key tidak harus permanen. Dalam banyak kasus, cukup beberapa jam hingga beberapa hari, tergantung sifat operasi bisnis. Trade-off-nya:
- Terlalu pendek: retry terlambat bisa lolos sebagai request baru.
- Terlalu panjang: penyimpanan membesar dan bisa membatasi reuse key yang sebenarnya tidak lagi relevan.
Yang penting, TTL harus didokumentasikan dalam kontrak integrasi, agar klien tahu jendela aman untuk retry.
Timeout dan retry policy yang aman
Jangan retry membabi buta
Retry aman hanya jika dua syarat terpenuhi:
- Klien tahu jenis kegagalan yang terjadi bersifat sementara.
- Operasi aman untuk diulang, biasanya karena idempoten.
Contoh kasus yang umumnya bisa dipertimbangkan untuk retry:
- Timeout jaringan sebelum respons diterima.
- HTTP 429 dengan petunjuk retry.
- HTTP 502, 503, atau 504 dari lapisan upstream.
Contoh yang umumnya jangan di-retry otomatis tanpa analisis:
- HTTP 400 karena input salah.
- HTTP 401/403 karena otorisasi gagal.
- HTTP 409 yang menandakan konflik bisnis.
- HTTP 422 karena validasi domain gagal.
Kontrak error yang dapat diotomasi agen
Format error berikut cukup praktis untuk agen:
HTTP/1.1 503 Service Unavailable
Content-Type: application/json
Retry-After: 10
{
"error": {
"code": "UPSTREAM_TIMEOUT",
"message": "Layanan eksternal belum merespons tepat waktu.",
"retryable": true,
"details": {
"retry_after_seconds": 10
}
}
}Dengan struktur seperti ini, agen atau orchestrator dapat mengambil keputusan otomatis: menunggu, mencoba lagi, atau meminta intervensi manusia.
Gunakan backoff dan batas retry
Retry tanpa batas akan memperburuk gangguan. Gunakan exponential backoff dengan jitter agar ledakan retry dari banyak worker tidak datang bersamaan. Simpan juga batas retry yang jelas di sisi klien maupun server.
Jika operasi penting tetapi hasil belum pasti, lebih baik beralih ke model submit once, poll status daripada terus menembakkan request eksekusi baru.
Status asynchronous dan polling yang stabil
Model kontrak yang disarankan
Untuk tool call yang lambat, gunakan pola berikut:
POST /tool-executionsmengembalikan202 Accepted.- Respons berisi
operation_iddanstatus_url. - Klien memanggil
GET /operations/{id}sampai status final. - Jika perlu, server juga mengirim webhook saat selesai.
Contoh status endpoint:
GET /v1/operations/op_9d13 HTTP/1.1
HTTP/1.1 200 OK
Content-Type: application/json
{
"operation_id": "op_9d13",
"status": "succeeded",
"result": {
"ticket_id": "tic_7712"
},
"finished_at": "2026-08-14T10:15:30Z"
}Status yang umum dan mudah dipahami mesin:
pending: diterima, belum mulai atau masih menunggu.running: sedang diproses.succeeded: selesai sukses.failed: selesai gagal permanen.canceled: dibatalkan.
Hindari status terlalu banyak tanpa kebutuhan nyata. Semakin banyak variasi state, semakin sulit agen membuat keputusan yang konsisten.
Mengapa polling sering lebih aman daripada hanya webhook
Webhook berguna, tetapi tidak cukup sebagai satu-satunya sumber kebenaran. Callback bisa terlambat, gagal terkirim, atau dikirim dua kali. Endpoint status memberi cara deterministik bagi agen untuk memverifikasi keadaan akhir.
Praktik yang aman: perlakukan webhook sebagai sinyal perubahan, tetapi verifikasi state final melalui endpoint status atau penyimpanan state internal yang konsisten.
Deduplikasi event dan race condition antar callback
Masalah umum pada webhook
Provider atau worker internal sering mengirim event lebih dari sekali. Bahkan jika event tidak duplikat, urutannya bisa terbalik. Misalnya, event completed datang lebih dulu daripada running karena keterlambatan jaringan.
Jika consumer langsung menulis state terakhir yang datang tanpa aturan, state akhir bisa mundur atau salah.
Pola deduplikasi event
Setiap event sebaiknya memiliki event_id unik. Consumer menyimpan daftar event yang sudah diproses, biasanya dalam tabel deduplikasi atau store cepat seperti Redis plus penyimpanan persisten bila dibutuhkan audit.
{
"event_id": "evt_20491",
"event_type": "tool_execution.completed",
"operation_id": "op_9d13",
"occurred_at": "2026-08-14T10:15:30Z",
"data": {
"ticket_id": "tic_7712"
}
}Aturan minimumnya:
- Jika
event_idsudah pernah diproses, abaikan. - Catat hasil pemrosesan agar event replay tidak memicu side effect lagi.
- Jangan mengandalkan hanya timestamp untuk deduplikasi.
Mencegah state mundur karena event tidak berurutan
Selain deduplikasi, Anda perlu aturan transisi state. Misalnya, succeeded dan failed adalah state terminal. Setelah operasi masuk state terminal, event running yang datang terlambat tidak boleh menimpa state.
Pendekatan yang umum:
- Gunakan state machine sederhana dengan transisi yang diizinkan.
- Pakai optimistic locking atau compare-and-set ketika update row.
- Simpan version atau sequence number jika publisher dapat menyediakannya.
Jika sequence number tersedia, consumer bisa menolak event dengan versi lebih rendah daripada versi yang sudah tersimpan. Jika tidak ada, endpoint status tetap menjadi sumber kebenaran untuk rekonsiliasi.
Contoh alur request/response yang tahan timeout dan duplikasi
Skenario: agen membuat tiket dukungan
- Agen memanggil
POST /v1/tool-executionsdenganIdempotency-Key. - Server menyimpan key, membuat
operation_id, lalu mengembalikan202 Accepted. - Terjadi timeout di sisi agen sebelum respons terbaca.
- Agen mengirim ulang request yang sama dengan key yang sama.
- Server menemukan operasi yang sama dan mengembalikan
operation_idyang sama, tanpa membuat tiket kedua. - Worker backend menyelesaikan proses dan mengirim webhook
tool_execution.completed. - Webhook terkirim dua kali. Consumer memproses sekali saja berdasarkan
event_id. - Agen atau orchestrator memanggil
GET /operations/{id}untuk memastikan status final.
Dengan pola ini, tidak ada pembuatan tiket ganda meskipun terjadi timeout, retry, dan webhook dobel.
Implementasi backend: penyimpanan idempotency key dan status operasi
Skema data minimal
Implementasi persisnya bebas, tetapi secara konsep Anda biasanya membutuhkan dua entitas:
- idempotency_records: menyimpan key, fingerprint request, status awal, dan referensi ke operasi atau respons.
- operations: menyimpan state asynchronous, hasil akhir, error, dan metadata waktu.
Contoh struktur konseptual:
idempotency_records
- idempotency_key (unique)
- request_fingerprint
- operation_id
- response_snapshot
- status
- created_at
- expires_at
operations
- operation_id (unique)
- type
- status
- input_snapshot
- result_snapshot
- error_code
- error_message
- created_at
- updated_at
- finished_atTransaksi dan kunci konkurensi
Masalah umum terjadi ketika dua request identik masuk hampir bersamaan sebelum record idempotensi tersimpan. Solusinya biasanya kombinasi dari:
- unique constraint pada
idempotency_key, - transaksi database saat membuat record awal,
- atau distributed lock jika beban dan arsitektur menuntutnya.
Prinsipnya: penentuan “request ini baru atau pengulangan” harus dilakukan secara atomik. Jika tidak, dua proses bisa sama-sama merasa menjadi eksekusi pertama.
Pseudocode handler yang aman
function handleCreateToolExecution(request) {
key = request.headers['Idempotency-Key']
fingerprint = hash(normalize(request.body))
begin transaction
record = findIdempotencyRecordForUpdate(key)
if (!record) {
operation = createOperation(status='pending', input=request.body)
createIdempotencyRecord(key, fingerprint, operation.id, status='accepted')
commit transaction
enqueue(operation.id)
return 202 with operation.id
}
if (record.request_fingerprint != fingerprint) {
rollback transaction
return 409 conflict
}
operation = findOperation(record.operation_id)
commit transaction
return 202 or final response derived from operation
}Poin pentingnya bukan sintaks, tetapi urutan logikanya: cek record secara atomik, validasi fingerprint, lalu kembalikan operasi yang sama.
Kontrak error yang ramah agen dan manusia
Supaya agen dapat mengambil tindakan otomatis, definisikan katalog error yang sempit dan stabil. Jangan biarkan semua kegagalan jatuh menjadi 500 dengan teks berbeda-beda.
Contoh kategori yang berguna:
- VALIDATION_ERROR: input salah, jangan retry.
- AUTHENTICATION_FAILED: token salah atau hilang, butuh perbaikan kredensial.
- RATE_LIMITED: retry setelah jeda.
- UPSTREAM_TIMEOUT: retry aman jika operasi idempoten.
- CONFLICT: ada konflik state atau aturan bisnis, perlu rekonsiliasi.
- INTERNAL_ERROR: kegagalan tak terduga, retry tergantung endpoint dan idempotensi.
Tambahkan metadata yang membantu otomasi tanpa membocorkan detail sensitif, misalnya retryable, retry_after_seconds, dan operation_id jika eksekusi tetap berlanjut di belakang layar.
Observability minimum yang wajib ada
Tanpa observability, idempotensi dan retry sulit diverifikasi saat insiden. Setidaknya sediakan:
- request_id untuk tiap request masuk,
- idempotency_key di log jika ada,
- operation_id untuk pekerjaan async,
- event_id untuk webhook/event,
- status akhir, durasi, dan penyebab gagal,
- counter untuk retry, duplicate hit, conflict, dan timeout.
Secara praktis, ini memungkinkan Anda menjawab pertanyaan penting saat debugging:
- Apakah request benar-benar diproses atau hanya timeout di jaringan?
- Apakah duplicate side effect terjadi karena key tidak dikirim, key tidak disimpan, atau race condition?
- Apakah webhook diproses ganda, atau state berubah mundur karena event terlambat?
Contoh log terstruktur
{
"request_id": "req_781",
"idempotency_key": "7f1f5bb2-3f2f-4a2f-a8a1-8d2f2d9c4a01",
"operation_id": "op_9d13",
"endpoint": "/v1/tool-executions",
"status": 202,
"result": "accepted",
"duration_ms": 34
}Jangan log payload sensitif secara mentah. Simpan fingerprint atau field yang aman bila cukup untuk korelasi.
Checklist desain kontrak API untuk workflow agentic
- Apakah semua endpoint yang punya side effect mendukung idempotensi?
- Apakah ada Idempotency-Key atau alternatif kunci bisnis yang jelas?
- Apakah reuse key dengan payload berbeda ditolak secara eksplisit?
- Apakah operasi lambat menggunakan
202 Accepteddan endpoint status? - Apakah status operasi memiliki state yang sederhana dan stabil?
- Apakah error memiliki
codeyang konsisten danretryableyang jelas? - Apakah retry policy dibatasi dan memakai backoff?
- Apakah webhook punya
event_idunik untuk deduplikasi? - Apakah transisi state mencegah state mundur akibat event tidak berurutan?
- Apakah ada observability minimum: request_id, operation_id, event_id, idempotency_key?
- Apakah ada unique constraint atau mekanisme atomik untuk mencegah race pada key?
- Apakah TTL idempotency key didokumentasikan?
Anti-pattern yang sering membuat integrasi agent tidak andal
1. Menganggap timeout berarti gagal
Timeout hanya berarti klien tidak mendapatkan jawaban tepat waktu. Server mungkin masih memproses atau bahkan sudah berhasil. Jika klien langsung menembakkan create baru tanpa idempotensi, duplikasi hampir pasti terjadi.
2. Mengirim webhook tanpa event_id
Tanpa identitas event yang stabil, consumer sulit membedakan antara pengiriman ulang dan event baru. Mengandalkan timestamp atau payload mentah sering tidak cukup.
3. Retry otomatis untuk semua 5xx tanpa idempotensi
Ini berbahaya untuk endpoint yang memicu side effect. Retry hanya aman jika operasi memang dirancang idempoten.
4. Menyimpan status akhir hanya di memori worker
Jika worker restart, agen kehilangan sumber kebenaran. Status operasi harus berada di penyimpanan yang dapat di-query ulang.
5. Error message bebas tanpa code stabil
Agen tidak bisa membuat keputusan andal jika hanya menerima teks error yang berubah-ubah. Gunakan kode error yang terstandar.
6. Membiarkan event lama menimpa state terminal
Tanpa aturan transisi state, callback terlambat dapat merusak state yang sebenarnya sudah final.
Penutup
Merancang kontrak API untuk agen AI berarti menerima bahwa jaringan tidak selalu pasti, agen akan melakukan retry, dan event dapat datang terlambat atau ganda. Karena itu, desain yang aman bukan sekadar endpoint yang berhasil pada jalur normal, tetapi kontrak yang tetap benar saat request diulang, respons terlambat, dan callback saling balapan.
Jika Anda hanya menerapkan tiga hal dari artikel ini, mulai dari sini: gunakan idempotency key untuk operasi tulis, ubah pekerjaan lambat menjadi asynchronous dengan endpoint status, dan buat error contract yang dapat dibaca mesin. Tiga fondasi ini sudah menghilangkan sebagian besar sumber duplicate side effect dan membuat workflow agentic jauh lebih andal di produksi.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!