Strategi kontrak API webhook harus memberikan kepastian kepada pengirim maupun penerima bahwa setiap notifikasi ditangani satu kali dan dapat diulang tanpa merusak keadaan sistem. Dalam kontrak ini, kita menetapkan skema backoff retry eksplisit, cara verifikasi payload melalui token dan signature, serta mekanisme idempotensi yang ditulis ke dalam status webhook untuk menghindari efek samping saat terjadi kegagalan.

Dengan pendekatan ini, sudah sejak awal jelas bagaimana header dan body ditetapkan, bagaimana status webhook bergerak dari pending ke success atau retry, serta bagaimana handler memeriksa ulang idempotensi dan mencatat hasil. Seluruh detail ini menjaga agar webhook tetap aman, terukur, dan mudah dipantau.

Rancang Kontrak Webhook yang Eksplisit

Kontrak harus mendokumentasikan header yang akan dikirim, struktur payload, tipe status, serta metadata retry. Contoh header/body minimal:

POST /webhook/handlers/order-status HTTP/1.1
Host: api.penerima-sumber.com
Content-Type: application/json
X-Webhook-Id: 6f9e97a1
X-Webhook-Signature: sha256=abcdef...
X-Webhook-Retry: 2

{
  "event": "order.completed",
  "timestamp": "2024-10-05T14:32:00Z",
  "order_id": "ORD-123",
  "payload": {
    "total": 259900,
    "currency": "IDR"
  }
}

Pastikan kontrak mencatat:

  • X-Webhook-Id unik untuk setiap event, dipakai untuk idempotensi.
  • X-Webhook-Signature menghitung HMAC atas body menggunakan shared secret.
  • X-Webhook-Retry opsi tambahan untuk menyertakan nomor percobaan, memudahkan logging.

Status webhook harus mengikuti pola: receivedprocessingsuccess atau retry/failed. Dokumen kontrak juga menyebutkan timeout maksimum yang diperbolehkan di sisi penerima agar pengirim tidak menunggu terlalu lama.

Backoff Retry

Setiap retry harus didorong oleh pola backoff yang dibahas dalam kontrak. Jangan hanya mengandalkan retry otomatis tanpa jeda karena bisa memberatkan sistem penerima. Contohnya, kontrak bisa menyarankan backoff eksponensial terbatas:

  • Delay awal 1s: 1000ms
  • Naik 2x setiap retry hingga maksimum 32s
  • Batasi jumlah retry (misal 5) sebelum status masuk failed

Bagian pentingnya adalah menyimpan waktu next_attempt_at di metadata dan menginformasikan status ke dashboard agar operator tahu pesan mana yang sedang menunggu retry.

Untuk memudahkan debugging, kontrak juga bisa mencakup header tambahan seperti X-Webhook-Last-Status dan X-Webhook-Last-Error agar penerima dapat memberi tahu kembali ke pengirim alasan kegagalan jika tersedia.

Idempotensi dan Penyimpanan Status

Ketika webhook diterima berulang kali, handler harus memeriksa apakah event tersebut sudah diproses. Kontrak menekankan penyimpanan status per X-Webhook-Id di basis data yang mendukung atomicity, misalnya menggunakan row-level lock atau upsert untuk menghindari race condition.

Pseudocode handler untuk idempotensi, penyimpanan status, dan backoff:

function handleWebhook(request):
  id = request.headers['X-Webhook-Id']
  job = db.findOrCreateWebhookJob(id)

  if job.status == 'success':
    return respond(200, 'Already processed')

  if job.status == 'processing' and withinLockWindow(job):
    return respond(202, 'Another worker is processing')

  job.status = 'processing'
  job.attempts += 1
  job.last_attempt_at = now()
  job.save()

  try:
    processPayload(request.json)
    job.status = 'success'
    job.completed_at = now()
    job.save()
    return respond(200, 'OK')
  except TransientError as err:
    job.status = 'retry'
    job.next_attempt_at = now() + backoff(job.attempts)
    job.error_message = err.message
    job.save()
    return respond(503, 'Retry scheduled')
  except PermanentError as err:
    job.status = 'failed'
    job.error_message = err.message
    job.save()
    return respond(400, 'Permanent failure')

Pastikan backoff menggunakan rumus eksponensial dengan batas atas agar tidak memicu beban berlebih. Pemrosesan payload harus dilengkapi dengan lock logis, misalnya memanfaatkan kolom processing_token dan timeout lock untuk mencegah race condition.

Verifikasi Payload

Verifikasi memastikan bahwa webhook tidak dimodifikasi. Dalam kontrak, definisikan:

  • Algoritme signature: misalnya HMAC-SHA256 dengan shared secret.
  • Header signature yang wajib diproses.
  • Jam kedaluwarsa pesan untuk mencegah replay attack.

Contoh dokumen kontrak:

  • Shared Secret: tidak pernah dikirim lewat webhook. Distributor menyimpan rahasia dari sistem penerima.
  • Header: X-Webhook-Signature berformat sha256=.
  • Verifikasi: penerima menghitung HMAC atas body dan membandingkan dengan nilai header sebelum lanjut memroses.

Jika signature gagal, segera respon 401 Unauthorized dan log peristiwa untuk audit. Dokumentasikan langkah penggantian secret dan rotasi keys agar tim operasi dapat merespons insiden dengan cepat.

Mencegah Race Condition dan Timeout Layanan Downstream

Race condition muncul saat beberapa worker memproses webhook sama sekali. Atasi dengan:

  • Locks di database menggunakan SELECT FOR UPDATE atau mekanisme distributed lock dengan TTL.
  • Kolom processing_token yang menyimpan ID worker aktif dan timestamp.
  • Timeout pemrosesan yang memicu rollback jika berhasil memproses tapi tidak sempat update status.

Untuk menangani timeout layanan downstream, kontrak menjelaskan batas waktu (misalnya 3 detik) per HTTP request. Jika layanan downstream melebihi, handler harus menganggapnya sebagai TransientError agar job masuk status retry dengan backoff. Dalam log, rekam downstream_endpoint, latency, dan respons terakhir untuk diagnosa.

Monitoring Kegagalan dan Duplikat

Monitoring memberikan visibilitas atas event webhook yang bermasalah:

  • Grafik jumlah webhook per status (pending, retry, failed).
  • Peringatan saat retry count melewati ambang tertentu dalam window waktu.
  • Log detail duplikat yang tertangani untuk memastikan idempotensi berjalan.

Gunakan metrik seperti webhook_processing_duration dan webhook_failure_rate, serta integrasi ke sistem monitoring agar alert muncul saat webhook menumpuk. Simpan log signature verification failure untuk audit keamanan.

Kesimpulan

Kontrak API webhook yang tangguh menggabungkan dokumentasi header/body, pola status yang jelas, strategi backoff, verifikasi signature, dan idempotensi berbasis status yang disimpan. Tambahkan praktik pencegahan race condition, penanganan timeout downstream, serta monitoring kegagalan dan duplikat untuk menjamin reliability. Dengan pendekatan ini, integrasi webhook tetap aman dan dapat diandalkan dalam kondisi nyata.