Lewati ke konten utama

Ikhtisar API

Halaman ini menjadi titik awal integrasi bagi tim frontend dan backend mitra. HTTP API dipasang di root environment dan mencakup rute JSON serta streaming SSE. Ingesti mitra Kafka/DMS memiliki kontrak event terpisah. Consumer menerima doc.uploaded tanpa aiDocumentId dan membuat ID tersebut setelah persistensi untuk event status tahapan. Tim frontend tidak memerlukan akses repository untuk berintegrasi; gunakan base URL environment target, kredensial, dokumen OpenAPI, dan halaman rute yang ditautkan di bawah.

Hal yang perlu diminta dari tim platform​

Sebelum menulis kode klien, mintalah hal berikut kepada tim platform:

ItemAlasan penting
Base URLOrigin yang meng-host API, misalnya https://rag.example.gov. Rute dipasang langsung di root.
Nama dan nilai header API keyRute data memerlukan header shared key yang dikonfigurasi. Nama header default adalah X-API-Key, tetapi perlakukan nama yang dikonfigurasi sebagai sumber kebenaran.
Rencana injeksi header identitasHeader X-User-* tepercaya dan menjadi bagian penting hanya ketika autentikasi terverifikasi dikonfigurasi dan infrastruktur tepercaya menyuntikkannya. Dengan autentikasi terverifikasi nonaktif (require_auth=false), pemfilteran RBAC/ABAC pada pengambilan data mengizinkan semua akses. Browser tidak boleh menetapkan header ini secara langsung.
Model origin deploymentKlien browser memerlukan hosting same-origin, backend-for-frontend (BFF), atau gateway yang menyuntikkan kredensial dan header CORS.
Identifier collectionNilai collection_ids opsional mempersempit /query dan /search ke collection mitra bernama. ID tidak dikenal yang tidak kosong menghasilkan tanpa baris. Penyembunyian collection terlarang berdasarkan akses hanya berlaku ketika pemfilteran terverifikasi diaktifkan; dalam mode izinkan semua tidak ada pemfilteran collection terlarang.
Pemetaan UUID dokumen AI engineNilai document_ids opsional mempersempit /query, /query/stream, dan /search ke nilai documents.id AI engine yang dipilih. Mitra memetakan record registry-nya ke aiDocumentId Kafka setelah persistensi; status upload menampilkan nilai yang sama sebagai stored_document_id, sedangkan hasil pengambilan dan sitasi menyebutnya document_id. ID yang formatnya benar tetapi tidak dikenal atau terlarang menghasilkan tanpa baris, bukan 403.
Batas upload dan ingestion URLUkuran file, ukuran batch, pengaktifan ingestion URL, dan allow-list URL merupakan konfigurasi environment.

Base URL dan path root​

Base URL environment diberikan oleh tim platform. Produksi melayani frontend di https://rag-aceh-neo.reviewstagepro.com (Studio di /studio) pada apps-0002 (audit produksi firstmate 2026-10-07); endpoint review-staging lama di bawah sudah dihapus:

EnvironmentBase URLStatus
Review staginghttps://rag-aceh-ai-eng.reviewstagepro.comDihapus 2026-10-07
Productionhttps://rag-aceh-ai-eng-neo.reviewstagepro.comAktif (ref 2cc9a417)

Rute FastAPI langsung menggunakan https://rag-aceh-ai-eng-neo.reviewstagepro.com dan dipasang di root origin tersebut. Frontend dan Studio tetap di https://rag-aceh-neo.reviewstagepro.com; BFF frontend hanya mem-proxy panggilan RAG melalui /api/rag/*, dan path GET yang tidak cocok mengembalikan HTML SPA. Rute FastAPI langsung saat ini tidak memiliki prefix /api, /api/v1, tenant, atau versi.

BASE_URL=https://rag-aceh-ai-eng-neo.reviewstagepro.com
GET ${BASE_URL}/healthz
GET ${BASE_URL}/readyz
POST ${BASE_URL}/query
POST ${BASE_URL}/query/stream
POST ${BASE_URL}/query/v2
POST ${BASE_URL}/playground # hanya administrator terverifikasi
POST ${BASE_URL}/search

Origin produksi mengakhiri TLS pada port 443 dan mempertahankan respons SSE. Port 80 ditutup; jangan mencoba memanggil port aplikasi internal atau port model secara langsung.

Untuk BFF frontend yang didokumentasikan, gunakan path proxy pada origin yang sama:

await fetch("/api/rag/query", { method: "POST", headers, body });

Gunakan base URL lengkap ketika adapter sisi server memanggil API dari layanan lain.

Autentikasi dan identitas tepercaya​

Setiap rute data memerlukan header API key yang dikonfigurasi. /healthz dan /readyz tidak memerlukan API key. Saat api_docs_enabled=true, /openapi.json, /docs, /docs/oauth2-redirect, dan /redoc juga tersedia tanpa API key; pengaturan ini saat ini aktif di produksi. Saat dinonaktifkan, rute dokumentasi tidak tersedia.

X-API-Key: <shared API key>

API key mengautentikasi sistem pemanggil. API key bukan identitas pengguna akhir. Header X-User-* menjadi tepercaya dan penting hanya ketika autentikasi terverifikasi dikonfigurasi serta infrastruktur tepercaya menghapus salinan yang dikirim pemanggil sebelum menyuntikkan klaim terverifikasi. Dengan autentikasi terverifikasi nonaktif, pemfilteran RBAC/ABAC pada pengambilan data mengizinkan semua akses, sehingga header identitas yang dapat dipalsukan tidak menyembunyikan hasil berdasarkan kebijakan akses. Rute yang membuat, membaca, melanjutkan, atau menghapus data milik pengguna juga menetapkan principal dari header identitas:

HeaderWajib ketika identitas diinjeksi?Makna
X-User-SubjectYaSubject pengguna terautentikasi yang stabil. Kepemilikan percakapan, kepemilikan memori, dan record audit terikat pada nilai ini.
X-User-RoleTidakRole yang digunakan kebijakan akses. Default ke user jika tidak ada.
X-User-UnitTidakUnit organisasi yang digunakan aturan visibilitas ABAC.
X-User-LevelTidakLevel posisi bilangan bulat yang digunakan aturan visibilitas ABAC.

Beberapa body request masih menyertakan field atau parameter kueri user_id. Perlakukan ini hanya sebagai fallback development. Ketika principal tepercaya yang terverifikasi berhasil ditetapkan, X-User-Subject diutamakan dan user_id yang diberikan pemanggil tidak memberikan akses ke percakapan atau memori pengguna lain.

Aturan keamanan browser​

Jangan mengirim shared API key ke JavaScript browser. Jangan izinkan pengguna menetapkan atau mengganti header X-User-* dari browser. Gunakan salah satu pola deployment berikut:

  1. BFF same-origin: browser memanggil backend aplikasi Anda; backend mengautentikasi sesi browser, menambahkan X-API-Key serta header X-User-* tepercaya, lalu meneruskannya ke API.
  2. Injeksi API gateway: gateway memvalidasi sesi atau token pengguna, menyuntikkan shared key dan header identitas, lalu menghapus salinan header tersebut yang dikirim pengguna.
  3. Integrasi sisi server: job terjadwal dan backend mitra memanggil API secara langsung dengan shared key, tetapi tetap menggunakan sumber identitas tepercaya untuk tindakan yang membawa principal.

Jenis content​

PermukaanJenis content requestJenis content responseCatatan
Rute JSONapplication/jsonapplication/jsonKirim JSON untuk /query, /search, /documents/txt, percakapan, memori, batch, dan ingestion URL.
Upload multipartmultipart/form-dataapplication/jsonPOST /documents dan POST /ocr menerima upload file. Biarkan fetch atau browser menetapkan multipart boundary.
Query streamapplication/jsontext/event-streamPOST /query/stream mengembalikan Server-Sent Events. Gunakan fetch() ditambah ReadableStream, bukan EventSource native, karena request memerlukan header dan body POST.

Server bersikap permisif jika Content-Type request tidak ada, tetapi klien tetap sebaiknya mengirim jenis yang benar demi portabilitas dan kompatibilitas gateway.

Persyaratan deployment CORS​

Aplikasi tidak menambahkan header CORS tingkat aplikasi. Jika halaman browser di-host pada origin yang berbeda dari API, preflight OPTIONS browser akan gagal kecuali gateway atau BFF Anda menangani CORS.

Deployment browser yang direkomendasikan:

  • host frontend dan API di balik origin yang sama; atau
  • tempatkan BFF/gateway pada origin frontend yang meneruskan request ke API; atau
  • konfigurasikan gateway untuk menjawab preflight CORS dan menyuntikkan kredensial dengan aman.

Jangan mengatasi CORS dengan mengekspos shared API key dalam kode browser.

OpenAPI dan Swagger​

RuteKetersediaanPenggunaan
/openapi.jsonapi_docs_enabled=true (saat ini aktif di produksi)Skema terkini yang dapat dibaca mesin. Jika memungkinkan, buat klien dari skema ini untuk setiap environment target.
/docsapi_docs_enabled=true (saat ini aktif di produksi)Swagger UI untuk eksplorasi interaktif dan percobaan request cepat.
/redocapi_docs_enabled=true (saat ini aktif di produksi)Referensi API ReDoc.

Rute OpenAPI mendokumentasikan skema dan bentuk rute, tetapi catatan integrasi di bagian ini tetap penting untuk perilaku gateway, keamanan browser, siklus hidup SSE, CORS, semantik kepemilikan, dan keterbatasan saat ini.

Fetch JSON cepat​

Gunakan kode sisi server atau BFF untuk pola ini. Kode browser tidak boleh memuat shared key.

curl -s -X POST "$BASE_URL/query" \
-H "Content-Type: application/json" \
-H "X-API-Key: $API_KEY" \
-H "X-User-Subject: 00000000-0000-0000-0000-000000000001" \
-H "X-User-Role: user" \
-d '{
"query": "Apa isi Qanun Aceh tentang Baitul Mal?",
"conversation_id": null,
"collection_ids": null
}'
type Citation = {
document_id: string;
document_title: string;
chunk_id: string;
chunk_ordinal: number;
page_number: number | null;
section_path: string[];
legal_reference: string | null;
char_start: number | null;
char_end: number | null;
confidential?: boolean;
owning_org_unit?: string;
min_position_level?: number;
};

type QueryResponse = {
answer: string;
conversation_id: string;
message_id: string;
citations: Citation[];
fallback_used: boolean;
fallback_reason: string | null;
dropped_claims_count: number;
};

export async function askQuestion(query: string): Promise<QueryResponse> {
const response = await fetch("/api/rag/query", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ query }),
});

if (!response.ok) {
const message = await readApiError(response);
throw new Error(message);
}

return (await response.json()) as QueryResponse;
}

Untuk parsing error, lihat Parsing error TypeScript.

Ringkasan siklus hidup SSE​

POST /query/stream menggunakan body request JSON yang sama seperti POST /query, tetapi mengembalikan text/event-stream. Lihat kontrak Query stream yang otoritatif untuk progres langsung, penyembunyian konten artifact, frame jawaban yang lolos guard, dan error terminal.

Objek sitasi​

Jawaban query, hasil pencarian, pesan percakapan, dan event SSE citation menggunakan bentuk objek sitasi yang sama.

FieldTipeSelalu ada?
document_idstring (UUID)Ya
document_titlestringYa
chunk_idstring (UUID)Ya
chunk_ordinalnumberYa
page_numbernumber | nullYa
section_pathstring[]Ya
legal_referencestring | nullYa
char_startnumber | nullYa
char_endnumber | nullYa
confidentialbooleanHanya ketika dokumen yang disitasi bersifat rahasia
owning_org_unitstringHanya ketika dokumen yang disitasi dibatasi ke unit organisasi
min_position_levelnumberHanya ketika dokumen memiliki level posisi minimum

Lihat objek sitasi bersama untuk contoh khusus rute.

Integrasi event mitra​

Indeks endpoint di bawah hanya mencantumkan rute HTTP dan SSE. Kontrak ingestion mitra Kafka/DMS yang dimaksud didokumentasikan secara terpisah di Event mitra. Consumer mengharapkan topic input doc.uploaded yang dikonfigurasi dan menerbitkan tujuan status khusus tahap (doc.extracted, doc.ocr.pending, doc.ocr.done, doc.chunked, doc.embedded, dan doc.indexed). Consumer menerima input tanpa aiDocumentId dan membuat ID tersebut setelah persistensi untuk event status tahapan.

Indeks endpoint​

MetodePathTujuanKontrak terperinci
GET/healthzLiveness proses dan metadata environment.Halaman ini: health dan discovery.
GET/readyzReadiness dependency PostgreSQL, embedding, reranker, Langfuse, dan Redis.Halaman ini: health dan discovery.
GET/openapi.jsonSkema OpenAPI terkini saat api_docs_enabled=true.Halaman ini: OpenAPI dan Swagger.
GET/docsSwagger UI saat api_docs_enabled=true.Halaman ini: OpenAPI dan Swagger.
GET/redocReferensi ReDoc saat api_docs_enabled=true.Halaman ini: OpenAPI dan Swagger.
POST/queryJawaban JSON dengan percakapan dan sitasi.Query dan pencarian.
POST/query/streamStream jawaban SSE dengan sitasi dan delta.Query dan pencarian.
POST/searchHasil pengambilan mentah tanpa sintesis.Query dan pencarian.
POST/generateGenerasi teks langsung. Bukan permukaan tanya-jawab: tanpa pengambilan, tanpa sitasi.Halaman ini: generasi langsung dan daftar model.
GET/modelsID model yang dikonfigurasi untuk dilayani engine ini, per role.Halaman ini: generasi langsung dan daftar model.
POST/embeddingsVektor untuk teks yang dikirimkan.Halaman ini: primitif model.
POST/rerankDokumen yang dikirimkan dinilai terhadap query dan diurutkan.Halaman ini: primitif model.
POST/conversationsMembuat percakapan secara eksplisit.Percakapan dan memori.
GET/conversationsMencantumkan percakapan pemanggil.Percakapan dan memori.
GET/conversations/{conversation_id}Mendapatkan satu percakapan milik pemanggil beserta pesannya.Percakapan dan memori.
DELETE/conversations/{conversation_id}Menghapus satu percakapan milik pemanggil beserta semua pesannya.Percakapan dan memori.
GET/memoriesMencantumkan memori jangka panjang pemanggil.Percakapan dan memori.
DELETE/memories/{memory_id}Menghapus satu memori milik pemanggil.Percakapan dan memori.
DELETE/memoriesPenghapusan semua memori pemanggil melalui fitur lupakan saya.Percakapan dan memori.
GET/quality/flaggedDaftar admin atas jawaban yang tidak disukai pengguna, terfilter dan ber-paging keyset.Peninjauan kualitas jawaban.
GET/quality/flagged/{message_id}Detail admin satu jawaban yang ditandai beserta akar masalah dan riwayat tinjauan.Peninjauan kualitas jawaban.
POST/quality/flagged/{message_id}/reviewLangkah tinjauan admin: menetapkan status, menambah catatan atau tautan issue.Peninjauan kualitas jawaban.
POST/documents/txtMengingesti satu dokumen teks inline secara sinkron.Ingesti dan dokumen.
POST/documentsMeng-upload satu atau beberapa file sebagai job ingestion asinkron.Ingesti dan dokumen.
GET/documentsMencantumkan dokumen yang di-upload/diingest, opsional difilter berdasarkan status.Ingesti dan dokumen.
GET/documents/{document_id}Membaca satu record dokumen ingestion.Ingesti dan dokumen.
DELETE/documents/{document_id}Menghapus permanen dokumen terindeks, chunk-nya, dan objek mentahnya jika ada.Ingesti dan dokumen.
POST/ocrEkstraksi khusus OCR dari satu file upload: teks plus confidence, tanpa embedding atau indexing.Ingesti dan dokumen.
POST/ingestion/batchMengantrikan ingestion dari prefix object storage atau manifest key.Ingesti dan dokumen.
POST/ingestion/urlMengantrikan ingestion URL yang ada di allow-list jika diaktifkan.Ingesti dan dokumen.
GET/ingestion/jobs/{job_id}Melakukan polling job ingestion.Ingesti dan dokumen.

Generasi langsung dan daftar model​

POST /generate menjalankan language model yang di-host sendiri menggunakan prompt yang Anda berikan. Gunakan untuk pekerjaan generasi yang bukan tanya-jawab dokumen: membuat boilerplate, memformat ulang atau menulis ulang teks yang sudah Anda miliki, menerjemahkan, atau mengklasifikasikan sebuah field.

/generate bukan permukaan tanya-jawab

Tidak ada yang diambil, sehingga respons membawa tanpa sitasi dan tidak diperiksa faithfulness-nya. Tidak ada konteks yang diambil untuk menjadi dasar faithfulness. Setiap respons menyertakan "grounded": false; lakukan percabangan berdasarkan field itu, bukan berdasarkan tidak adanya sitasi. Jangan tampilkan output /generate kepada warga seolah-olah bersumber dari JDIH, PPID, atau corpus resmi lainnya. Untuk jawaban berbasis sumber dan bersitasi atas corpus dokumen, panggil POST /query.

Menempelkan chunk hasil pengambilan dari POST /search ke dalam prompt lalu merender hasilnya sebagai jawaban berada di luar kontrak ini. Engine tidak dapat mendeteksinya; tanggung jawab berada pada aplikasi yang mengonsumsi.

POST /generate adalah panggilan model mentah yang tetap diautentikasi. Engine tidak menambahkan system framing, output Policy Guard, rewrite, atau penulisan audit. system_prompt dari pemanggil dikirim apa adanya sebelum prompt pengguna, dan completion model dikembalikan tanpa perubahan. Aplikasi pemanggil bertanggung jawab atas moderasi, audit, atau kontrol publikasi yang dibutuhkannya.

Hanya kegagalan bentuk request dan runtime model yang tersisa:

KondisiStatus
Runtime serving menolak request karena lebih panjang dari context window413
Body tidak valid, prompt tidak ada, atau tipe field salah422
Endpoint model atau model yang dipilih tidak tersedia502

Completion yang terpotong oleh budget token mengembalikan truncated: true; teksnya tidak lengkap, bukan sekadar pendek.

curl -s -X POST "$BASE_URL/generate" \
-H "Content-Type: application/json" \
-H "X-API-Key: $API_KEY" \
-d '{"prompt": "Buat draf surat tugas perjalanan dinas.", "max_tokens": 512}'

ID model bergantung pada deployment; contoh ini menggunakan profil A30 staging saat ini.

{
"text": "Draf surat tugas ...",
"model": "qwen3-14b",
"finish_reason": "stop",
"truncated": false,
"grounded": false
}

GET /models mencantumkan model yang dikonfigurasi per role. usable_for_generation menandai saran model chat yang dikonfigurasi; field ini bukan allow-list. POST /generate meneruskan ID model dari pemanggil ke runtime, yang dapat menolaknya. Daftar ini melaporkan konfigurasi, bukan liveness model. Gunakan /readyz, bukan /healthz, untuk readiness dependency.

Health dan discovery​

GET /healthz mengembalikan 200 ketika proses API berjalan dan melaporkan konfigurasi backend aktif. Endpoint ini adalah probe liveness dan sengaja tidak menghubungi dependency.

{
"status": "ok",
"version": "<api version>",
"env": "local | test | staging | production",
"repository_backend": "memory | postgres",
"object_storage_backend": "memory | s3"
}

GET /readyz menjalankan lima pemeriksaan independen secara konkuren, masing-masing dengan timeout dua detik: PostgreSQL SELECT 1, rute /health embedding dan reranker, Langfuse /api/public/health ketika Langfuse diaktifkan, serta Redis PING terautentikasi. Endpoint ini mengembalikan 200 hanya jika semua pemeriksaan wajib berhasil. Jika ada pemeriksaan gagal atau timeout, endpoint mengembalikan 503, tetap menampilkan hasil yang berhasil, dan mencantumkan semua dependency yang gagal tanpa mengekspos kredensial.

{
"status": "not_ready",
"dependencies": {
"postgres": { "healthy": true, "detail": "SELECT 1 succeeded" },
"embedding": { "healthy": false, "detail": "dependency check failed" },
"reranker": { "healthy": true, "detail": "health endpoint returned 200" },
"langfuse": { "healthy": true, "detail": "health endpoint returned 200" },
"redis": { "healthy": true, "detail": "PING succeeded" }
},
"failed_dependencies": ["embedding"]
}

Gunakan /healthz untuk liveness proses dan /readyz untuk readiness load balancer, Compose, serta deployment. Saat api_docs_enabled=true, gunakan /openapi.json untuk menemukan skema environment target; produksi saat ini mengaktifkannya.

Primitif model​

POST /embeddings dan POST /rerank mengekspos model embedding dan reranking engine secara langsung untuk aplikasi yang memerlukan vektor atau skor relevansi, bukan jawaban. Keduanya memerlukan header API key dan merupakan primitif: keduanya tidak mengambil data dari corpus, menambahkan sitasi, menjalankan pemeriksaan faithfulness, atau menerapkan Government Policy Guard. Gunakan POST /query ketika memerlukan jawaban, dan POST /search untuk mengurutkan corpus yang sudah diingest.

POST /embeddings menerima {"input": ["text", ...]} dan mengembalikan vektor dalam urutan request, bersama nama model dan lebar vektor:

{ "model": "bge-m3", "dimensions": 1024, "embeddings": [[0.01, -0.02, "..."]] }

200 selalu membawa tepat satu vektor per input, dalam urutan request, pada lebar tetap deployment. Jika backend mengembalikan jumlah vektor yang berbeda dari jumlah teks yang dikirim, lebar berbeda, atau elemen non-finite, rute gagal dengan 502 yang menyebutkan hasil yang dikembalikan. Daftar tidak pernah dipendekkan atau disejajarkan ulang, sehingga embeddings[i] selalu sesuai dengan input[i].

POST /rerank menerima {"query": "...", "documents": ["text", ...]} dan mengembalikan dokumen yang diurutkan berdasarkan relevansi menurun, masing-masing dengan indeksnya dalam request:

{ "model": "bge-reranker-v2-m3", "results": [{ "index": 1, "relevance_score": 0.97, "text": "..." }] }

200 selalu memberi skor pada setiap dokumen yang dikirim tepat satu kali. Dua jumlah diperiksa: backend harus mengembalikan jumlah hasil sebanyak dokumen yang dikirim, dan hasil tersebut harus mencakup setiap indeks yang dikirim. Reranker biasanya hanya mengembalikan top-N miliknya; jika backend memberi skor lebih sedikit dari jumlah dokumen yang dikirim, mengulang indeks, mengembalikan indeks di luar request, atau mengembalikan skor non-finite, rute gagal dengan 502 yang menyebutkan kedua jumlah tersebut, bukan mengembalikan peringkat parsial yang tampak lengkap, atau peringkat tanpa duplikasi yang skor terbuangnya bertentangan dengan skor yang diberikan.

Kedua rute menolak request yang melebihi budget, bukan memangkasnya. Default-nya adalah 64 item, 8000 karakter per item (rerank query dihitung sebagai item), 64.000 karakter per request, dan, karena reranker menilai query + dokumen sebagai satu sequence, 8000 karakter per pasangan query/dokumen. Deployment dapat memperketat batas ini (ACEH_RAG_PRIMITIVES__*). Melebihi budget ukuran deployment menghasilkan 413, dengan detail string yang menyebutkan batas yang dilampaui dan ukuran yang melampauinya; melanggar skema yang dipublikasikan, termasuk batas absolutnya yang dapat diperketat deployment tetapi tidak dapat dinaikkan, menghasilkan 422 dari pydantic dengan detail berupa daftar objek error. Backend model yang tidak tersedia menghasilkan 502.

Perhatikan bahwa budget pasangan secara default sama dengan 8000 seperti batas per item, sehingga dokumen dengan panjang maksimum hanya dapat di-rerank dengan query kosong, yang dilarang skema. Ukur ukuran dokumen terhadap budget pasangan, bukan batas per item.

Batas ini membatasi yang diterima API dalam karakter. Batas ini bukan janji tentang tokenizer model: backend embedding yang dilayani (TEI) berjalan dengan --auto-truncate dan reranker (Infinity) memotong secara internal ke panjang sequence maksimum model, sehingga teks yang sesuai batas karakter tetapi melebihi token window model akan dipotong backend sebelum di-embed atau diberi skor. Tetap jauh di bawah batas jika fidelity input yang tepat penting bagi use case Anda.

Keterbatasan saat ini​

Ini adalah batasan yang dapat diamati saat ini, bukan jaminan masa depan:

  • Tanpa prefix versi: rute dipasang di root dan tidak diberi namespace di bawah /api/v1.
  • Tanpa kontrol pagination: rute list mengembalikan daftar penuh saat ini untuk pemanggil atau filter. Tidak ada parameter cursor, limit, atau offset.
  • Tanpa header rate-limit: respons tidak menyertakan X-RateLimit-* atau Retry-After sebagai kontrak standar.
  • Tanpa header request ID: respons tidak menyertakan header ID request/korelasi standar. Jika gateway menambahkannya, perlakukan sebagai spesifik gateway.
  • Tanpa rute WebSocket: streaming menggunakan SSE pada POST /query/stream; tidak ada API WebSocket.
  • Tanpa CORS tingkat aplikasi: deployment browser lintas origin memerlukan penanganan CORS oleh gateway/BFF.
  • Dokumentasi bergantung pada konfigurasi: /openapi.json, /docs, dan /redoc memerlukan api_docs_enabled=true, yang saat ini aktif di produksi.