Desain API agent browser yang aman perlu mengatasi masalah yang jarang muncul pada integrasi manual: aksi bisa terkirim dua kali, callback bisa terlambat, urutan event bisa berubah, dan kredensial tool bisa bocor. Jika API Anda dipakai oleh agent browser, automation runner, atau server tooling yang menjalankan aksi atas nama pengguna, maka kontrak API harus tahan terhadap retry, aman untuk akses terlingkup, dan mudah diaudit.
Artikel ini membahas pola praktis untuk merancang API yang stabil dan aman, terinspirasi oleh ekosistem developer yang makin banyak memakai agent browser dan tooling otomatis. Fokusnya bukan pada berita atau produk tertentu, tetapi pada keputusan desain backend: scoped auth, token per tool, idempotency key, verifikasi webhook, retry dengan backoff, deduplikasi event, timeout, dan audit log.
Mengapa API untuk agent browser berbeda dari integrasi biasa
Pada integrasi klasik, klien biasanya berupa aplikasi frontend atau server internal yang perilakunya relatif dapat diprediksi. Pada agent browser, ada lapisan orkestrasi tambahan: tool pemanggil, runtime agent, antrean pekerjaan, browser automation, dan callback asinkron. Akibatnya, API harus siap menghadapi kondisi berikut:
- Double-submit: aksi yang sama dikirim dua kali karena retry jaringan atau bug orchestration.
- Callback terlambat: hasil pekerjaan datang setelah klien sudah timeout atau menganggap job gagal.
- Urutan event tidak konsisten: event
completedbisa tiba sebelumstartedkarena jalur pemrosesan berbeda. - Secret bocor: token tool tersimpan di log, prompt, atau sistem pihak ketiga.
- Delivery at-least-once: webhook dapat dikirim lebih dari sekali dan tetap harus aman diproses.
Karena itu, desain API sebaiknya menganggap bahwa request, event, dan callback tidak selalu unik, tidak selalu berurutan, dan tidak selalu tepat waktu.
Prinsip kontrak API yang stabil
1. Pisahkan resource sinkron dan job asinkron
Untuk aksi browser atau tooling yang bisa memakan waktu lama, hindari endpoint yang memblokir sampai semua proses selesai. Lebih aman memakai pola job:
POST /v1/browser-actionsuntuk membuat jobGET /v1/browser-actions/{id}untuk mengambil status job- Webhook untuk notifikasi perubahan status
Pola ini bekerja karena timeout HTTP klien tidak memengaruhi pemrosesan backend. Jika klien putus koneksi, job tetap bisa berjalan dan hasilnya dapat diambil ulang.
2. Gunakan status yang eksplisit dan terminal yang jelas
Jangan hanya memakai success dan failed. Gunakan state machine sederhana yang mudah diaudit, misalnya:
accepted: request diterimaqueued: menunggu workerrunning: sedang diprosessucceeded: selesai suksesfailed: selesai gagalcanceled: dibatalkanexpired: tidak valid lagi karena TTL terlewati
State terminal harus jelas. Jika job sudah succeeded, webhook ulang dengan payload lama tidak boleh mengubah state menjadi running.
3. Versikan API tanpa mematahkan klien
Letakkan versi di path seperti /v1 atau lewat header versi yang konsisten. Tambahkan field baru dengan cara kompatibel ke belakang. Hindari mengubah makna field lama. Untuk agent browser, kestabilan kontrak lebih penting daripada desain payload yang terlalu “rapi”.
Scoped auth dan token per tool
Mengapa token global berbahaya
Kesalahan umum adalah memberikan satu API key penuh ke semua tool automation. Jika satu tool bocor atau salah konfigurasi, seluruh akun terdampak. Untuk integrasi agent browser, lebih aman memakai token per tool dengan scope minimum.
Desain scope yang praktis
Scope sebaiknya mengikuti aksi bisnis, bukan hanya role umum. Contoh:
browser_actions:createbrowser_actions:readwebhooks:managesessions:read
Jika tool hanya perlu membuat job dan membaca statusnya, jangan berikan hak mengelola webhook atau membaca semua data akun.
Contoh header autentikasi
POST /v1/browser-actions HTTP/1.1
Host: api.example.com
Authorization: Bearer tkn_tool_abc123
Content-Type: application/json
Idempotency-Key: 8a0d0ec6-4ce5-4a24-9f91-8d4e2f0d89d8
X-Request-Id: req_01J...
Praktik yang disarankan:
- Setiap token punya owner, tool_id, dan scope yang tercatat.
- Simpan token dalam bentuk hash, bukan plaintext, jika sistem Anda menerbitkannya sendiri.
- Dukung rotasi token tanpa downtime.
- Berikan metadata seperti nama tool, tanggal pembuatan, dan last used untuk audit.
Jika secret bocor
Anda harus menganggap kebocoran token sebagai kejadian operasional yang akan terjadi. Mitigasinya:
- Token dibatasi scope dan akun yang spesifik.
- Token dapat dicabut cepat tanpa memengaruhi tool lain.
- Audit log menunjukkan token mana yang dipakai untuk request tertentu.
- Rate limit dan anomali penggunaan dapat mendeteksi penyalahgunaan.
Jangan kirim token ke webhook target, jangan menaruh token di query string, dan jangan mencetak header
Authorizationke log aplikasi.
Idempotency untuk aksi berulang dan double-submit
Kapan idempotency wajib
Jika endpoint memicu efek samping, seperti membuat job browser, mengisi form, mengirim data, atau memulai sesi automation, maka idempotency key wajib dipertimbangkan. Ini melindungi sistem dari retry klien, timeout jaringan, dan klik ganda.
Kontrak yang aman
Klien mengirim header Idempotency-Key yang unik untuk satu niat aksi. Server menyimpan kombinasi berikut:
- idempotency key
- identitas pemanggil atau token
- fingerprint request, misalnya hash body yang dinormalisasi
- hasil respons pertama
- waktu kedaluwarsa key
Jika request yang sama datang lagi dengan key yang sama dan body identik, server mengembalikan respons sebelumnya. Jika key sama tetapi body berbeda, balas dengan error konflik karena ada indikasi bug klien atau penyalahgunaan.
Contoh request membuat job
POST /v1/browser-actions
Authorization: Bearer tkn_tool_abc123
Content-Type: application/json
Idempotency-Key: 8a0d0ec6-4ce5-4a24-9f91-8d4e2f0d89d8
{
"action_type": "open_page_and_extract",
"target_url": "https://example.org/dashboard",
"callback_url": "https://tool-client.example/webhooks/browser-actions",
"timeout_seconds": 45,
"input": {
"selector": "#report-table"
}
}
Contoh respons sukses awal
HTTP/1.1 202 Accepted
Content-Type: application/json
Location: /v1/browser-actions/act_123
{
"id": "act_123",
"status": "accepted",
"created_at": "2026-07-04T10:00:00Z",
"idempotency_key": "8a0d0ec6-4ce5-4a24-9f91-8d4e2f0d89d8"
}
Contoh jika key sama tetapi payload berbeda
HTTP/1.1 409 Conflict
Content-Type: application/json
{
"error": {
"code": "idempotency_key_reused_with_different_payload",
"message": "Idempotency-Key sudah pernah dipakai untuk payload yang berbeda"
}
}
Mengapa ini penting: tanpa idempotency, retry otomatis dari client library atau API gateway dapat membuat dua job identik yang sama-sama berjalan, lalu memicu dua webhook dan dua perubahan data downstream.
Webhook yang terverifikasi dan tahan duplikasi
Desain webhook yang aman
Webhook tidak boleh dianggap tepercaya hanya karena datang dari internet ke endpoint tertentu. Setidaknya, lakukan:
- HTTPS wajib
- Signature verification dengan secret per endpoint atau per subscriber
- Timestamp validation untuk mencegah replay
- Event ID unik untuk deduplikasi
- Fast acknowledgment: kembalikan 2xx cepat, proses berat dilakukan async
Contoh header webhook
POST /webhooks/browser-actions HTTP/1.1
Host: tool-client.example
Content-Type: application/json
X-Webhook-Id: wh_01JABC...
X-Webhook-Timestamp: 1762221600
X-Webhook-Signature: v1=5f2c0d...
X-Event-Type: browser_action.succeeded
Contoh payload webhook
{
"event_id": "evt_01JABC...",
"event_type": "browser_action.succeeded",
"created_at": "2026-07-04T10:00:35Z",
"data": {
"action_id": "act_123",
"status": "succeeded",
"result": {
"title": "Dashboard",
"records_found": 12
}
}
}
Verifikasi signature
Pola umum yang aman adalah menghitung HMAC dari string yang mencakup timestamp dan body mentah. Contoh bentuk input:
signed_payload = timestamp + "." + raw_body
signature = HMAC_SHA256(webhook_secret, signed_payload)
Penerima webhook membandingkan signature header dengan hasil HMAC lokal memakai perbandingan konstan waktu, lalu memastikan timestamp masih dalam jendela replay yang diterima.
Mengapa body mentah penting: jika Anda mem-parse JSON lalu meng-serialize ulang, urutan field atau whitespace bisa berubah dan verifikasi gagal.
Deduplikasi event
Webhook hampir selalu harus diasumsikan at-least-once delivery. Simpan event_id yang sudah diproses. Jika event yang sama datang lagi, balas 200 OK atau 204 No Content tanpa memproses efek samping kedua kalinya.
Strategi deduplikasi minimal:
- Primary key atau unique index pada
event_id - Status pemrosesan event:
received,processing,processed,failed - Retry internal jika handler gagal setelah event diterima
Urutan event tidak konsisten
Jangan menulis handler yang bergantung pada urutan mutlak. Lebih aman jika setiap event diproses sebagai “sumber kebenaran lokal” yang memajukan state hanya jika transisinya valid. Contoh:
- Jika status sekarang
succeeded, abaikan eventrunningyang datang terlambat. - Jika event
completeddatang sebelumstarted, tetap terima jika transisinya terminal dan event memiliki timestamp atau sequence yang masuk akal.
Jika perlu, sertakan salah satu dari:
sequencemonotonik per actionoccurred_atyang akurat- versi state resource saat event dibuat
Namun, sequence saja tidak cukup bila ada retry lama yang baru terkirim kemudian. Handler tetap harus idempotent.
Retry dengan backoff, timeout, dan status code
Kapan boleh retry
Retry sebaiknya dilakukan untuk error sementara, bukan untuk semua kegagalan. Aturan praktis:
- Retry: timeout, koneksi putus,
429,502,503,504 - Jangan retry otomatis:
400,401,403,404,422kecuali ada alasan bisnis yang jelas
Backoff yang aman
Gunakan exponential backoff dengan jitter agar sistem tidak menciptakan lonjakan serentak setelah gangguan. Contoh urutan sederhana:
- percobaan 1: segera
- percobaan 2: ~1 detik
- percobaan 3: ~2 detik
- percobaan 4: ~4 detik
- tambahkan jitter acak kecil pada tiap langkah
Untuk webhook, sertakan batas retry dan kebijakan dead-letter atau penandaan gagal permanen jika endpoint subscriber terus bermasalah.
Timeout
Pisahkan beberapa jenis timeout:
- Request timeout di sisi API gateway atau klien
- Job timeout untuk lama eksekusi browser action
- Webhook delivery timeout saat menghubungi subscriber
Kesalahan umum adalah menyamakan timeout HTTP 10 detik dengan job timeout 10 detik. Untuk kerja asinkron, request boleh selesai cepat dengan 202 Accepted, sedangkan job berjalan lebih lama sesuai SLA internal.
Status code yang disarankan
202 Accepted: job diterima untuk diproses200 OK: status job berhasil diambil201 Created: resource sinkron berhasil dibuat204 No Content: webhook diterima tanpa body respons400 Bad Request: payload tidak valid secara sintaks401 Unauthorized: token tidak valid atau tidak ada403 Forbidden: token valid tapi scope kurang409 Conflict: konflik idempotency atau transisi state tidak valid422 Unprocessable Entity: payload valid secara sintaks, tapi melanggar aturan bisnis429 Too Many Requests: rate limit terlampaui500/502/503/504: kegagalan sementara yang bisa memicu retry
Contoh desain endpoint yang praktis
Membuat browser action
POST /v1/browser-actionsHeader:
Authorization: Bearer ...Idempotency-Key: ...X-Request-Id: ...opsional tapi berguna untuk tracing
Body:
{
"action_type": "open_page_and_extract",
"target_url": "https://example.org/dashboard",
"callback_url": "https://tool-client.example/webhooks/browser-actions",
"timeout_seconds": 45,
"input": {
"selector": "#report-table"
},
"metadata": {
"tool_run_id": "run_789"
}
}
Mengambil status action
GET /v1/browser-actions/act_123Contoh respons:
{
"id": "act_123",
"status": "running",
"action_type": "open_page_and_extract",
"created_at": "2026-07-04T10:00:00Z",
"updated_at": "2026-07-04T10:00:10Z",
"expires_at": "2026-07-04T10:05:00Z",
"last_error": null
}
Mendaftarkan endpoint webhook subscriber
POST /v1/webhook-endpointsBody:
{
"url": "https://tool-client.example/webhooks/browser-actions",
"event_types": [
"browser_action.accepted",
"browser_action.running",
"browser_action.succeeded",
"browser_action.failed"
]
}
Contoh respons:
{
"id": "whsub_123",
"url": "https://tool-client.example/webhooks/browser-actions",
"status": "active",
"secret_last4": "9f3a"
}
Secret webhook sebaiknya hanya ditampilkan sekali saat pembuatan atau rotasi.
Audit log dan observability
Apa yang perlu dicatat
Untuk sistem automation, audit log bukan tambahan opsional. Minimal catat:
- siapa atau token mana yang membuat job
- scope yang dipakai
- idempotency key
- request id dan trace id
- perubahan status job
- upaya pengiriman webhook dan hasilnya
- rotasi atau pencabutan secret
Audit log berbeda dari application log biasa. Audit log harus fokus pada jejak tindakan yang bisa ditelusuri kembali untuk keamanan dan forensik.
Jangan log data sensitif secara mentah
Masking diperlukan untuk:
- header
Authorization - secret webhook
- cookie atau session token
- payload yang mengandung kredensial atau data pribadi
Jika perlu menyimpan payload untuk debugging, pertimbangkan redaksi field sensitif sebelum disimpan.
Edge case yang sering merusak integrasi
1. Double-submit dari klien
Penyebabnya bisa timeout, retry library, atau pengguna menekan aksi berulang. Solusi utamanya adalah idempotency key dan penyimpanan hasil request pertama.
2. Callback terlambat setelah klien menganggap gagal
Klien mungkin menandai job gagal karena timeout lokal, padahal backend masih memproses dan akhirnya sukses. Solusinya:
- anggap webhook dan polling sebagai sumber kebenaran akhir
- jangan langsung menghapus konteks job hanya karena timeout request awal
- berikan TTL job dan status terminal yang eksplisit
3. Event datang tidak berurutan
Handler harus berbasis transisi state yang valid, bukan asumsi urutan kedatangan. Simpan timestamp atau sequence, tetapi tetap buat handler idempotent.
4. Secret bocor
Jika token atau webhook secret bocor:
- cabut atau rotasi secret segera
- cek audit log untuk pola request mencurigakan
- pastikan scope sempit agar dampak terbatas
- berikan mekanisme re-issue tanpa mengubah semua integrasi lain
5. Worker selesai, tetapi webhook gagal dikirim
Jangan gabungkan keberhasilan job dengan keberhasilan delivery webhook. Job bisa succeeded walau webhook ke subscriber gagal sementara. Simpan status delivery terpisah dan retry sesuai kebijakan.
Contoh pseudocode backend
Validasi idempotency saat create action
function createBrowserAction(request, token) {
requireScope(token, "browser_actions:create")
validate(request.body)
key = request.headers["Idempotency-Key"]
if (!key) return error(400, "missing_idempotency_key")
fingerprint = hashCanonicalJson(request.body)
existing = idempotencyStore.find(token.id, key)
if (existing) {
if (existing.fingerprint != fingerprint) {
return error(409, "idempotency_key_reused_with_different_payload")
}
return existing.response
}
action = insertAction({
status: "accepted",
requested_by_token_id: token.id,
payload: request.body
})
response = {
id: action.id,
status: action.status
}
idempotencyStore.save(token.id, key, fingerprint, response)
enqueue(action.id)
return accepted(response)
}
Verifikasi webhook masuk
function handleWebhook(rawBody, headers, secret) {
timestamp = headers["X-Webhook-Timestamp"]
signature = headers["X-Webhook-Signature"]
if (isExpired(timestamp)) {
return error(400, "stale_webhook")
}
expected = hmacSha256(secret, timestamp + "." + rawBody)
if (!constantTimeEquals(signature, "v1=" + expected)) {
return error(401, "invalid_signature")
}
event = parseJson(rawBody)
if (eventStore.alreadyProcessed(event.event_id)) {
return noContent()
}
eventStore.markReceived(event.event_id)
enqueueWebhookHandling(event)
return noContent()
}
Checklist implementasi backend
- Tentukan model resource asinkron dengan status terminal yang jelas.
- Versikan API dan hindari perubahan payload yang mematahkan klien.
- Terapkan token per tool, scope minimum, dan mekanisme rotasi.
- Simpan token dalam bentuk hash jika Anda menerbitkannya sendiri.
- Wajibkan
Idempotency-Keyuntuk endpoint yang menimbulkan efek samping. - Simpan fingerprint payload dan respons pertama untuk replay idempotent.
- Kembalikan
409 Conflictjika key sama dipakai untuk payload berbeda. - Dukung webhook dengan secret unik, signature HMAC, dan validasi timestamp.
- Simpan
event_iduntuk deduplikasi webhook. - Pastikan handler event idempotent dan tahan urutan event yang tidak konsisten.
- Gunakan retry dengan exponential backoff plus jitter untuk error sementara.
- Pisahkan timeout request, timeout job, dan timeout delivery webhook.
- Catat audit log untuk token, scope, request id, transisi state, dan rotasi secret.
- Masking semua secret dan kredensial dari log.
- Tambahkan rate limit dan deteksi anomali dasar per token atau tool.
- Sediakan endpoint status job agar klien tidak hanya bergantung pada webhook.
Penutup
Desain API agent browser yang aman bukan hanya soal autentikasi, tetapi soal membuat seluruh alur integrasi tahan terhadap kondisi nyata: retry, duplikasi, event terlambat, dan kebocoran secret. Kombinasi kontrak resource asinkron, scoped auth, token per tool, idempotency key, webhook terverifikasi, deduplikasi event, timeout yang tepat, dan audit log akan mengurangi bug operasional yang sulit dilacak.
Jika Anda sedang membangun integrasi untuk agent browser atau tooling otomatis, mulai dari yang paling berdampak: endpoint job yang stabil, idempotency di aksi tulis, dan verifikasi webhook yang benar. Tiga hal ini biasanya menyelesaikan sebagian besar masalah produksi sebelum sistem bertambah kompleks.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!