Desain API untuk Search Partner tidak boleh bergantung pada asumsi implisit. Ketika partner search, discovery, atau indexing berubah, integrasi yang tampak sederhana sering gagal karena kontrak payload tidak stabil, retry memicu duplikasi, webhook mudah dipalsukan, atau event datang tidak berurutan.

Jika Anda sedang membangun integrasi dengan partner search, jawaban singkatnya adalah ini: gunakan API contract yang tegas dan terversi, autentikasi antarlayanan yang dapat diaudit, idempotency key untuk submission, retry dengan exponential backoff dan batas yang jelas, serta webhook yang diverifikasi tanda tangannya dan tahan terhadap replay. Tanpa itu, integrasi akan rapuh saat partner menambah field, memperlambat respons, mengirim ulang event, atau mengalami partial failure.

Konteks penting: ekosistem mesin pencari dan discovery dapat berubah cepat. Munculnya pemain baru atau perubahan kebijakan distribusi adalah pengingat bahwa integrasi antarsistem harus dirancang untuk berubah, bukan diasumsikan statis.

Mengapa integrasi search partner sering gagal

Pada praktiknya, kegagalan bukan hanya karena server down. Penyebab yang lebih sering adalah:

  • Kontrak payload ambigu: field wajib tidak jelas, enum tidak terdokumentasi, atau semantik status berubah diam-diam.
  • Retry naif: request timeout langsung dikirim ulang tanpa idempotency, sehingga satu dokumen terindeks berkali-kali.
  • Webhook tidak aman: endpoint menerima request dari siapa pun selama format JSON benar.
  • Out-of-order delivery: event indexed tiba lebih dulu daripada submitted dalam pipeline observabilitas internal.
  • Partial failure: batch 100 item gagal untuk 7 item, tetapi API hanya mengembalikan satu status global.
  • Versioning tidak disiplin: perubahan kontrak “kecil” mematahkan parser atau validasi partner.

Karena itu, desain API untuk partner search harus mengutamakan predictability, auditability, dan toleransi terhadap perilaku jaringan yang tidak ideal.

Prinsip kontrak API yang tegas

1. Pisahkan operasi sinkron dan asinkron

Untuk indexing atau submission ke search partner, respons cepat lebih penting daripada hasil akhir langsung. Desain yang umum:

  • POST /submissions menerima dokumen atau URL untuk diproses.
  • API mengembalikan 202 Accepted jika pekerjaan diterima ke antrean.
  • Status akhir dikirim lewat webhook atau diambil lewat endpoint status.

Pendekatan ini bekerja karena indexing sering melibatkan validasi, deduplikasi, normalisasi, dan pemrosesan downstream yang tidak cocok dipaksa selesai dalam satu request sinkron.

2. Tentukan field wajib, opsional, dan aturan kompatibilitas

Kontrak harus menjelaskan dengan eksplisit:

  • Field wajib dan opsional.
  • Tipe data dan batasannya.
  • Format waktu, misalnya ISO 8601 UTC.
  • Apakah field baru boleh ditambahkan tanpa menaikkan versi mayor.
  • Apakah klien harus mengabaikan field yang tidak dikenal.

Aturan penting untuk kompatibilitas maju adalah: consumer harus toleran terhadap field baru, tetapi producer tidak boleh diam-diam mengubah makna field lama.

3. Gunakan identifier yang stabil

Bedakan beberapa jenis identifier:

  • submission_id: ID unik untuk satu permintaan submission.
  • document_id: ID domain Anda sendiri untuk konten yang sama di berbagai submission.
  • partner_reference: ID dari partner, jika mereka mengeluarkan referensi internal.
  • event_id: ID unik untuk setiap event webhook.

Kesalahan umum adalah memakai satu ID untuk semua kebutuhan. Itu membuat deduplikasi, audit, dan debugging menjadi kabur.

4. Bedakan status transport dan status bisnis

HTTP status code hanya menjelaskan hasil di level protokol dan pemrosesan awal, bukan status akhir indexing. Misalnya:

  • 202 Accepted: submission diterima, belum tentu berhasil diindeks.
  • 400 Bad Request: payload tidak valid.
  • 401 Unauthorized atau 403 Forbidden: kredensial salah atau tidak berhak.
  • 409 Conflict: konflik idempotency atau versi sumber daya.
  • 429 Too Many Requests: melewati batas laju.
  • 500/502/503/504: gangguan sisi server atau upstream.

Status bisnis seperti queued, processing, indexed, rejected, atau partial_success sebaiknya dikirim di body respons atau event webhook.

Contoh kontrak request/response untuk submission indexing

Request submission

POST /v1/submissions HTTP/1.1
Authorization: Bearer <service-token>
Content-Type: application/json
Idempotency-Key: 1d7a8e9d-4c31-4c4e-b8ad-7f7e12d9a100
X-Request-Id: req_01J2YB8D3H7K

{
  "submission_id": "sub_20260803_0001",
  "callback_url": "https://api.example.com/webhooks/search-partner",
  "documents": [
    {
      "document_id": "doc-1001",
      "url": "https://example.com/articles/desain-api-search",
      "title": "Desain API untuk Search Partner",
      "language": "id",
      "last_modified_at": "2026-08-03T08:00:00Z",
      "checksum": "sha256:8f5b..."
    },
    {
      "document_id": "doc-1002",
      "url": "https://example.com/articles/webhook-aman",
      "title": "Webhook Aman untuk Integrasi Partner",
      "language": "id",
      "last_modified_at": "2026-08-03T08:05:00Z",
      "checksum": "sha256:2c41..."
    }
  ]
}

Respons 202 Accepted

HTTP/1.1 202 Accepted
Content-Type: application/json
X-Request-Id: req_01J2YB8D3H7K

{
  "submission_id": "sub_20260803_0001",
  "status": "queued",
  "accepted_count": 2,
  "rejected_count": 0,
  "status_url": "/v1/submissions/sub_20260803_0001"
}

Mengapa 202 Accepted lebih tepat daripada 200 OK? Karena server baru menyatakan bahwa pekerjaan diterima untuk diproses, bukan bahwa dokumen sudah sukses diindeks.

Respons validasi gagal

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "error": {
    "code": "invalid_request",
    "message": "One or more documents are invalid",
    "details": [
      {
        "field": "documents[1].url",
        "reason": "must be a valid https URL"
      }
    ]
  }
}

Berikan error yang bisa ditindaklanjuti. Pesan seperti “invalid payload” tanpa lokasi field hanya memperlambat integrasi.

Respons konflik idempotency

HTTP/1.1 409 Conflict
Content-Type: application/json

{
  "error": {
    "code": "idempotency_conflict",
    "message": "Idempotency-Key was already used with a different payload"
  }
}

Ini penting agar klien tahu bahwa key yang sama tidak boleh dipakai untuk payload berbeda.

Versioning: kapan perlu versi baru dan bagaimana menjaganya tetap stabil

Untuk API partner, versioning eksplisit lebih aman daripada mengandalkan perubahan diam-diam. Pilihan paling mudah dipahami biasanya versi di path, misalnya /v1/submissions.

Aturan praktis versioning

  • Perubahan non-breaking: menambah field opsional baru, menambah enum baru yang sudah didokumentasikan sebagai extensible, menambah endpoint baru.
  • Perubahan breaking: mengubah tipe field, mengubah makna field, menghapus field, mengubah perilaku error yang diandalkan klien, atau memperketat validasi tanpa masa transisi.

Jika perubahan bersifat breaking, buat versi baru. Jangan memaksa semua partner menyesuaikan di hari yang sama.

Tambahkan kebijakan deprecation

Dokumentasikan setidaknya:

  • Tanggal pengumuman deprecation.
  • Batas akhir dukungan versi lama.
  • Perbedaan utama antarversi.
  • Cara migrasi dan contoh payload baru.

Kesalahan desain yang umum adalah menganggap “partner bisa cepat menyesuaikan”. Pada integrasi B2B atau antarlayanan lintas organisasi, asumsi itu sering salah.

Autentikasi antarlayanan: sederhana, bisa diaudit, dan aman

Untuk komunikasi server-ke-server, tujuan utamanya bukan hanya mengizinkan akses, tetapi juga memastikan setiap request bisa ditelusuri dan dicabut jika terjadi kebocoran.

Pilihan umum

  • Bearer token statis: mudah diterapkan, tetapi rotasi dan pembatasannya harus disiplin.
  • Signed request berbasis HMAC: cocok jika ingin integritas request dan verifikasi asal pengirim.
  • mTLS: kuat di level transport, tetapi operasionalnya lebih kompleks.

Jika kebutuhan Anda adalah integrasi partner yang relatif sedikit namun sensitif, kombinasi TLS + bearer token yang pendek masa berlakunya atau TLS + HMAC signature biasanya lebih realistis daripada langsung memulai dengan arsitektur yang terlalu rumit.

Prinsip yang sebaiknya ada

  • Semua request lewat HTTPS.
  • Kredensial punya scope yang terbatas.
  • Dukungan rotasi secret tanpa downtime.
  • Setiap request membawa X-Request-Id untuk korelasi log.
  • Jangan taruh secret di query string.

Idempotency key untuk submission dan indexing

Network timeout tidak selalu berarti request gagal. Bisa jadi server sudah menerima dan memproses request, tetapi responsnya tidak sempat kembali ke klien. Di sinilah idempotency key penting.

Cara kerja yang aman

  1. Klien mengirim Idempotency-Key yang unik untuk satu operasi logis.
  2. Server menyimpan kombinasi idempotency key + fingerprint payload + hasil respons.
  3. Jika request identik datang lagi dengan key yang sama, server mengembalikan hasil yang sama tanpa memproses ulang.
  4. Jika key sama tetapi payload berbeda, kembalikan 409 Conflict.

Jangan hanya menyimpan key tanpa fingerprint payload. Kalau tidak, klien bisa tidak sengaja menimpa operasi berbeda dengan key yang sama.

Apa yang perlu disimpan

  • Idempotency key.
  • Hash payload yang distabilkan.
  • Status akhir atau status pemrosesan sementara.
  • Timestamp pembuatan dan waktu kedaluwarsa.
  • Request ID untuk audit.

Masa simpan key tergantung kebutuhan bisnis, tetapi harus cukup lama untuk mencakup retry normal dan keterlambatan jaringan. Hindari masa simpan terlalu singkat yang membuat replay sah diproses ulang.

Retry dengan backoff: kapan boleh, kapan harus berhenti

Tidak semua kegagalan boleh di-retry. Retry yang salah justru memperburuk kemacetan dan menghasilkan duplikasi.

Error yang umumnya layak di-retry

  • 408 Request Timeout
  • 429 Too Many Requests
  • 502 Bad Gateway
  • 503 Service Unavailable
  • 504 Gateway Timeout
  • Kegagalan jaringan sementara sebelum respons diterima

Error yang umumnya tidak boleh di-retry tanpa perubahan

  • 400 Bad Request
  • 401 Unauthorized
  • 403 Forbidden
  • 404 Not Found jika endpoint memang salah
  • 422 Unprocessable Entity jika payload valid secara sintaks, tetapi salah secara domain

Gunakan exponential backoff dengan jitter

Backoff mencegah semua klien menyerbu server pada saat yang sama. Jitter menambahkan variasi acak agar retry tidak sinkron.

attempt 1: tunggu ~1 detik
attempt 2: tunggu ~2 detik
attempt 3: tunggu ~4 detik
attempt 4: tunggu ~8 detik
+ jitter acak kecil di setiap langkah

Jika partner mengirim header seperti Retry-After, hormati nilainya selama masih masuk akal untuk SLA Anda.

Batasi retry

Retry tanpa batas adalah antipola. Terapkan:

  • Batas jumlah percobaan.
  • Batas total durasi retry.
  • Circuit breaker atau penundaan sementara jika upstream terus gagal.
  • Dead-letter queue untuk kasus yang perlu investigasi manual.

Webhook aman: verifikasi tanda tangan, anti-replay, dan deduplikasi

Webhook adalah titik balik paling umum tempat integrasi partner bocor atau rusak. Jangan menganggap IP allowlist saja cukup, dan jangan percaya isi payload tanpa verifikasi.

Contoh webhook event

POST /webhooks/search-partner HTTP/1.1
Content-Type: application/json
X-Webhook-Id: wh_01J2YBWQ4E4S
X-Webhook-Timestamp: 2026-08-03T08:10:22Z
X-Webhook-Signature: v1=1f4a9b...

{
  "event_id": "evt_01J2YBWQ4E4S",
  "event_type": "document.indexed",
  "occurred_at": "2026-08-03T08:10:20Z",
  "submission_id": "sub_20260803_0001",
  "document_id": "doc-1001",
  "partner_reference": "p_884433",
  "status": "indexed"
}

Langkah verifikasi yang disarankan

  1. Ambil raw body persis seperti diterima, jangan JSON yang sudah diubah serialisasinya.
  2. Bangun string yang akan ditandatangani, misalnya gabungan timestamp dan raw body.
  3. Hitung HMAC dengan secret bersama.
  4. Bandingkan dengan header signature memakai perbandingan waktu konstan.
  5. Tolak jika timestamp terlalu lama atau terlalu jauh ke masa depan.
signed_payload = timestamp + "." + raw_body
expected_signature = HMAC_SHA256(secret, signed_payload)

Mengapa harus memakai raw body? Karena serialisasi JSON dapat berubah urutan field atau spasinya. Jika Anda menghitung signature dari objek yang sudah diparse lalu diserialisasi ulang, verifikasi bisa gagal walau payload asli valid.

Anti-replay

Signature saja belum cukup. Penyerang atau sistem perantara yang salah konfigurasi bisa mengirim ulang payload valid. Maka:

  • Terapkan batas usia timestamp, misalnya hanya menerima webhook dalam jendela waktu tertentu.
  • Simpan event_id atau webhook_id yang sudah diproses.
  • Tolak event duplikat atau tandai sebagai already processed.

Deduplikasi event

Deduplikasi sebaiknya dilakukan berdasarkan event_id, bukan hanya kombinasi document_id + status. Dua event berbeda bisa saja punya status yang sama tetapi waktu kejadian dan konteksnya berbeda.

Menangani edge case yang sering dilupakan

Replay event

Partner yang baik tetap mungkin mengirim ulang webhook jika mereka tidak menerima respons sukses. Perlakukan webhook sebagai at-least-once delivery. Artinya, handler Anda wajib idempoten.

Out-of-order delivery

Jangan asumsikan event datang berurutan. Misalnya, event document.indexed bisa tercatat lebih dulu daripada document.processing akibat retry atau jalur antrean berbeda.

Solusi umum:

  • Simpan occurred_at dan gunakan aturan transisi status yang aman.
  • Jika menerima status yang lebih “lama”, abaikan atau simpan sebagai event historis tanpa menurunkan status final.
  • Pertimbangkan sequence number jika partner mampu menyediakannya.

Timeout

Jika klien timeout saat memanggil submission API, jangan langsung mengasumsikan operasi gagal. Cek dengan salah satu cara berikut:

  • Ulangi request dengan Idempotency-Key yang sama.
  • Panggil GET /submissions/{submission_id} jika server menyediakan endpoint status.

Partial failure

Untuk batch submission, hindari satu status global yang menyembunyikan item gagal. Berikan hasil per item jika memungkinkan.

HTTP/1.1 202 Accepted
Content-Type: application/json

{
  "submission_id": "sub_20260803_0002",
  "status": "partial_success",
  "accepted_count": 8,
  "rejected_count": 2,
  "results": [
    {
      "document_id": "doc-2001",
      "status": "accepted"
    },
    {
      "document_id": "doc-2002",
      "status": "rejected",
      "error": {
        "code": "invalid_checksum",
        "message": "checksum format is invalid"
      }
    }
  ]
}

Trade-off-nya: payload respons lebih besar. Namun untuk integrasi partner, kejelasan biasanya lebih bernilai daripada menghemat beberapa kilobyte.

Race condition pada update status

Jika beberapa worker memproses event untuk dokumen yang sama, gunakan mekanisme konsistensi yang sesuai, misalnya:

  • Unique constraint untuk event_id.
  • Conditional update berdasarkan versi atau timestamp status terakhir.
  • Transaksi database untuk pembaruan state dan pencatatan event.

Status code yang tepat untuk integrasi partner

  • 200 OK: operasi sinkron sukses atau webhook diterima dan diproses.
  • 202 Accepted: request diterima untuk diproses asinkron.
  • 204 No Content: sukses tanpa body, misalnya callback acknowledgement minimalis.
  • 400 Bad Request: format atau parameter request salah.
  • 401 Unauthorized: autentikasi gagal atau token tidak ada.
  • 403 Forbidden: kredensial valid tetapi tidak punya akses.
  • 404 Not Found: resource atau endpoint tidak ditemukan.
  • 409 Conflict: konflik idempotency, state, atau versi.
  • 410 Gone: endpoint versi lama sudah dihentikan, bila ingin eksplisit.
  • 422 Unprocessable Entity: sintaks valid, tetapi aturan domain dilanggar.
  • 429 Too Many Requests: rate limit tercapai.
  • 500 Internal Server Error: error server yang tidak lebih spesifik.
  • 502/503/504: gangguan upstream atau ketersediaan sementara.

Kesalahan umum adalah mengembalikan 200 untuk semua keadaan lalu menaruh kegagalan di field success: false. Itu menyulitkan retry policy, observabilitas, dan penanganan oleh klien generik.

Pola implementasi yang praktis

1. Submission API menulis ke database lalu enqueue job

Alur yang relatif aman:

  1. Validasi request.
  2. Cek dan simpan idempotency key.
  3. Simpan submission beserta dokumen.
  4. Commit transaksi.
  5. Masukkan job ke antrean untuk diproses worker.
  6. Kembalikan 202 dengan status awal.

Menulis state sebelum enqueue membantu audit. Jika enqueue gagal setelah database commit, Anda bisa punya mekanisme kompensasi atau sweeper untuk mendeteksi submission yang belum pernah diproses.

2. Webhook receiver harus tipis dan cepat

Jangan lakukan logika berat langsung di handler HTTP webhook. Pola yang lebih tahan:

  1. Verifikasi signature dan timestamp.
  2. Cek deduplikasi event_id.
  3. Simpan payload mentah dan metadata.
  4. Kembalikan 2xx secepat mungkin jika diterima.
  5. Proses event di worker asinkron.

Ini mengurangi timeout dan mencegah partner mengirim ulang event karena endpoint Anda terlalu lambat.

3. Sediakan endpoint status untuk debugging dan rekonsiliasi

GET /v1/submissions/sub_20260803_0001 HTTP/1.1
Authorization: Bearer <service-token>
HTTP/1.1 200 OK
Content-Type: application/json

{
  "submission_id": "sub_20260803_0001",
  "status": "processing",
  "created_at": "2026-08-03T08:00:02Z",
  "documents": [
    {
      "document_id": "doc-1001",
      "status": "indexed",
      "updated_at": "2026-08-03T08:10:20Z"
    },
    {
      "document_id": "doc-1002",
      "status": "processing",
      "updated_at": "2026-08-03T08:09:01Z"
    }
  ]
}

Endpoint ini sangat membantu saat webhook tertunda, signature salah, atau partner menanyakan status item tertentu.

Checklist implementasi desain API untuk search partner

  • Kontrak request/response terdokumentasi jelas, termasuk field wajib, enum, dan format waktu.
  • API memiliki versioning eksplisit.
  • Semua request antarlayanan memakai HTTPS.
  • Autentikasi mendukung rotasi secret dan audit request.
  • Submission mendukung Idempotency-Key.
  • Server menyimpan fingerprint payload untuk mendeteksi konflik idempotency.
  • Retry hanya untuk error transient dan memakai exponential backoff + jitter.
  • Webhook diverifikasi dengan signature berbasis raw body.
  • Webhook memiliki proteksi replay berbasis timestamp dan event deduplication.
  • Handler webhook idempoten dan cepat mengembalikan 2xx.
  • Status bisnis dibedakan dari HTTP status.
  • Batch operation mendukung partial failure yang eksplisit.
  • Ada endpoint status untuk polling, debugging, atau rekonsiliasi.
  • Log menyertakan request_id, submission_id, document_id, dan event_id.
  • Ada metrik untuk latency, retry count, duplicate webhook, dan signature failure.

Kesalahan desain yang membuat integrasi partner rapuh

  • Mengandalkan timeout sebagai indikator gagal: padahal request mungkin sudah diproses.
  • Tidak memakai idempotency key untuk operasi create/submit.
  • Menggunakan 200 untuk semua respons.
  • Menganggap webhook exactly-once.
  • Memproses webhook berat di request thread.
  • Tidak menyimpan raw payload webhook, sehingga investigasi signature mismatch sulit.
  • Mengubah kontrak tanpa versi baru.
  • Menggabungkan identifier yang berbeda ke satu field serbaguna.
  • Tidak mendokumentasikan retry policy dan batas rate limit.
  • Tidak punya strategi partial failure untuk batch submission.

Tips debugging saat integrasi mulai bermasalah

  • Periksa apakah request timeout terjadi sebelum atau sesudah upstream menerima payload.
  • Bandingkan X-Request-Id di sisi klien dan server untuk korelasi log.
  • Untuk webhook, cek apakah signature dihitung dari raw body yang sama persis.
  • Audit event duplikat berdasarkan event_id, bukan hanya document_id.
  • Pastikan worker tidak menurunkan status final akibat event lama yang datang belakangan.
  • Periksa konflik idempotency: key sama, payload berubah.
  • Jika retry meningkat tajam, lihat apakah partner mengembalikan 429 atau 503 dan apakah Retry-After diikuti.

Penutup

Integrasi dengan search partner jarang gagal karena satu bug besar. Yang lebih sering terjadi adalah akumulasi keputusan kecil yang tampak sepele: kontrak longgar, retry tanpa idempotency, webhook tanpa verifikasi, dan asumsi bahwa event selalu datang sekali serta berurutan. Desain API untuk Search Partner yang kuat justru lahir dari disiplin terhadap detail tersebut.

Jika Anda hanya memperbaiki tiga hal, mulai dari sini: tegas pada kontrak dan versioning, jadikan submission idempoten, dan verifikasi webhook dengan proteksi replay. Tiga fondasi ini akan membuat integrasi jauh lebih tahan terhadap perubahan partner, gangguan jaringan, dan pertumbuhan volume di kemudian hari.