Dokumentasi ini ditujukan untuk tim IT di sisi tenant yang memiliki backend sendiri (sistem booking, CRM, ticketing, inventory, dsb.) dan ingin mengintegrasikannya dengan agent EMBAN AI melalui API.
01 Overview
1.1 Siapa Anda di dokumen ini
Anda adalah developer di sisi tenant yang punya backend sendiri (sistem booking, CRM, ticketing internal, inventory, dsb.) dan ingin agent CS EMBAN AI berinteraksi dengan backend itu lewat API.
Contoh skenario:
- Tim IT "Rent-a-Car X" yang sudah subscribe plan
csdan mau agent CS bisa booking mobil lewat API mereka sendiri. - Tim IT toko ritel yang mau agent CS bisa cek stok produk + menerbitkan tiket eskalasi ke help-desk internal.
Tugas teknis Anda:
- Host ticketing webhook yang menerima notifikasi
ticket.createddari EMBAN saat agent CS eskalasi ke human. - Membalas ticket via
POST /api/v1/tickets/:id/reply. - Host action endpoint yang menerima outbound request dari EMBAN saat customer memicu custom action (booking, cek stok, dsb).
- Mengirim callback via
POST /api/v1/callbacks/:submission_iduntuk action mode asinkron.
1.2 Aktor & Boundary
| Aktor | Siapa | Bicara ke EMBAN lewat |
|---|---|---|
| Tenant IT (Anda) | Developer di sisi tenant CS | HTTP API callback/reply + webhook receiver, HMAC per-tenant/per-action |
| Tenant owner | End-user yang bayar plan | WhatsApp / Telegram / Email β bukan API |
| Customer | Pelanggan akhir dari tenant CS | WhatsApp / Telegram / Email, direlay lewat tenant |
| EMBAN engine | Pusat orchestration | Menerima request dari Anda, memanggil endpoint Anda |
1.3 Base URL & Versioning
- Host canonical:
https://api.rentalai.id. Semua endpoint di-prefix/api/v1/... - Versi API saat ini: v1. Breaking change memicu versi baru; v1 tetap hidup minimal 6 bulan setelah sunset diumumkan.
β οΈ Jangan hardcode host di kode. Saat onboarding, Anda akan menerima webhook_base_url β pakai value itu, simpan di env.
1.4 Model Autentikasi (ringkasan)
| Secret | Skup | Dipakai di |
|---|---|---|
ticket_webhook_secret | tenant-scoped, 1 per tenant | Outbound ticket.created webhook + inbound /api/v1/tickets/:id/reply |
action.secret | action-scoped, N per tenant | Outbound action endpoint + inbound /api/v1/callbacks/:submission_id |
Semua autentikasi memakai HMAC-SHA256 dengan dua skema: Ts-prefixed (default, window 5 menit) dan Body-only (Action Engine outbound).
1.5 Standard Response Envelope
Sukses:
{
"ok": true,
"tenant_id": "01J...",
"...": "field-domain-specific"
}
Error:
{
"ok": false,
"error_code": "schema_invalid",
"message": "Human-readable reason",
"request_id": "req_01J...",
"details": { "...": "opsional" }
}
1.6 Rate Limit & Idempotency
- Kuota standar: 60 req/menit (
API_RATE_LIMIT_STANDARD). - Semua endpoint write menerima header
Idempotency-Key(UUID/ULID). Permintaan ulang dengan key sama dalam 24 jam mengembalikan response cached. - Retry: eksponensial (2s, 4s, 8s, 16s) maksimal 4 kali.
1.7 Hello World β Cek Health
Endpoint GET /api/v1/health adalah public, tanpa HMAC:
curl -sS https://api.rentalai.id/api/v1/health
# { "ok": true, "status": "healthy", "version": "1.0.0", ... }
1.8 Urutan Membaca Dokumen
Saran urutan: #1 β #2 β #5 β #12 (untuk read-only integrasi) β #3 β #6 β #4 β #7 (skim) β #8 β #9 β #11
02 Authentication (HMAC)
Semua endpoint write dan webhook diautentikasi pakai HMAC-SHA256 signature. Tidak ada OAuth, tidak ada bearer token. Pola ini mengikuti konvensi Stripe/Slack β sederhana, stateless, dan tahan terhadap replay.
2.1 Dua Jenis Secret
| Nama Logis | Scope | Dipakai di |
|---|---|---|
| Ticket webhook secret | per-tenant | Outbound ticket.created webhook + inbound POST /api/v1/tickets/:id/reply |
| Action callback secret | per-action, per-tenant | Outbound action endpoint + inbound POST /api/v1/callbacks/:submission_id |
2.2 Dua Skema Tanda Tangan
Ts-prefixed scheme (default)
Dipakai oleh: ticket webhook, ticket reply, callback async action, file delivery.
signing_input = <timestamp_ms> + "." + <raw_request_body>
signature = "sha256=" + hex( HMAC_SHA256(secret, signing_input) )
timestamp_msβ Unix millis (string angka desimal)raw_request_bodyβ exact bytes dari body. Jangan re-stringify.
Body-only scheme (Action Engine outbound saja)
signing_input = <raw_request_body>
signature = "sha256=" + hex( HMAC_SHA256(secret, raw_request_body) )
2.3 Header Wajib
Outbound dari Anda ke EMBAN:
| Header | Nilai |
|---|---|
X-EMBAN-Timestamp | Unix millis (ts-prefixed saja) |
X-EMBAN-Signature | sha256=<hex-digest> |
Content-Type | application/json |
Idempotency-Key | UUID/ULID (sangat direkomendasikan) |
2.4 Toleransi Skew & Replay
- Default window: 5 menit (
API_HMAC_TIMESTAMP_SKEW_MS=300000) - Jam server wajib tersinkron NTP. Drift > 5 menit = semua request ditolak.
2.5 Contoh Node.js β Sign Outbound
import { createHmac, randomUUID } from "node:crypto";
function signRequest(body, secret) {
const ts = Date.now();
const raw = typeof body === "string" ? body : JSON.stringify(body);
const sig = "sha256=" +
createHmac("sha256", secret).update(`${ts}.${raw}`).digest("hex");
return { ts, raw, sig };
}
async function replyToTicket(baseUrl, ticketId, secret, payload) {
const { ts, raw, sig } = signRequest(payload, secret);
const res = await fetch(`${baseUrl}/api/v1/tickets/${ticketId}/reply`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-EMBAN-Timestamp": String(ts),
"X-EMBAN-Signature": sig,
"Idempotency-Key": randomUUID(),
},
body: raw,
});
return res.json();
}
β οΈ Pitfall: Jangan kirim body: payload (object) β framework akan re-stringify dengan urutan key berbeda, signature tidak cocok. Sign dulu, kirim string yang sama persis.
2.6 Contoh Python β Sign Outbound
import hmac, hashlib, json, time, uuid, requests
def sign_request(body, secret: str):
ts = int(time.time() * 1000)
raw = body if isinstance(body, str) else json.dumps(body, separators=(",", ":"))
sig = "sha256=" + hmac.new(
secret.encode(), f"{ts}.{raw}".encode(), hashlib.sha256
).hexdigest()
return ts, raw, sig
2.7 Contoh curl + openssl
SECRET="your-secret"
TS=$(date +%s%3N)
BODY='{"status":"success","data":{"booking_id":"BK-2026-0412"}}'
SIG="sha256=$(printf '%s.%s' "$TS" "$BODY" \
| openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $2}')"
curl -sS -X POST "$BASE/api/v1/callbacks/$SUBMISSION_ID" \
-H "Content-Type: application/json" \
-H "X-EMBAN-Timestamp: $TS" \
-H "X-EMBAN-Signature: $SIG" \
--data-binary "$BODY"
2.8 Error Autentikasi
| HTTP | error_code | Artinya |
|---|---|---|
| 401 | missing_auth_headers | Header signature/timestamp absen |
| 401 | invalid_timestamp | Timestamp bukan angka |
| 401 | timestamp_skew | Drift > 5 menit β sync NTP |
| 401 | unknown_signer | Secret tidak dikenali untuk tenant ini |
| 401 | invalid_signature | Signature mismatch β cek body bytes, secret, hex lowercase, prefix sha256=, skema benar |
2.9 Rotasi Secret
- Koordinasi per tenant β secret diubah di env EMBAN + sisi Anda dalam satu operasi
- Cadence rekomendasi: 90 hari, lebih sering jika ada regulatory requirement
- Secret jangan di-commit ke git. Kalau terlanjur, rotate dulu baru rewrite history
03 Callbacks API (Async Action)
Endpoint ini dipanggil Tenant IT setelah EMBAN memicu action asinkron ke backend Tenant IT. Alur: customer → EMBAN → Tenant IT → EMBAN → customer.
3.1 Terminologi
| Istilah | Artinya |
|---|---|
| Action | Spesifikasi kustom tenant: endpoint + fields + templates. Row di tenant_actions. |
| Submission | Satu instansi eksekusi action. Status: collecting → submitted → awaiting_callback → callback_delivered. |
| Sync mode | EMBAN tunggu response HTTP langsung. Tidak ada callback. |
| Async mode | EMBAN balas immediate_ack, lalu tunggu callback dari Tenant IT. |
3.2 Endpoint
POST /api/v1/callbacks/:submission_id
submission_iddiberikan di outbound request pertama (headerX-EMBAN-Submission-Id)- Secret untuk HMAC = value dari
action.endpoint.auth.secret_env - Skema: ts-prefixed
3.3 Request Body
| Field | Tipe | Wajib | Catatan |
|---|---|---|---|
status | "success" | "error" | default "success" | Menentukan template |
template_key | string | optional | Override eksplisit nama template |
data | object | default {} | Payload untuk interpolasi template |
error_code | string | optional | Dicari di error_templates |
error_message | string | optional | Tersedia sebagai {{error_message}} |
files | array ≤ 10 | optional | Attachment untuk customer |
Contoh success:
{
"status": "success",
"template_key": "booking_confirmed",
"data": {
"booking_id": "BK-2026-0412",
"car_model": "Toyota Avanza",
"pickup_date": "22 April 2026",
"price_idr": "850.000"
}
}
3.4 Template Rendering (Mustache-lite)
{{field}}→data.field{{obj.nested.key}}→ nested paths OK- Value
null/undefined→ empty string - Tidak ada loop, conditional, atau partial β sengaja sederhana
3.5 File Attachment
{ "url": "https://files.tenant.example/invoice.pdf",
"filename": "invoice.pdf",
"mime_type": "application/pdf",
"expected_size_bytes": 524288 }
| Aturan | Nilai |
|---|---|
| Protokol | HTTPS wajib |
| Hostname | Harus cocok allowlist per-tenant |
| Max size per file | 10 MB |
| Max files per callback | 10 |
| MIME allowlist | PDF, PNG, JPG, MP3, MP4, docx, xlsx, zip |
| TTL di disk EMBAN | 24 jam (auto-delete) |
3.6 Error Codes
| HTTP | error_code | Aksi |
|---|---|---|
| 400 | schema_invalid | Perbaiki payload |
| 404 | submission_not_found | Cek submission_id |
| 409 | invalid_submission_state | Jangan retry β state tidak akan balik |
| 503 | delivery_unavailable | Retry dengan backoff |
3.7 Retry & Idempotency
- Retry maks 4× dengan backoff 2s/4s/8s/16s untuk 5xx
Idempotency-Keywajib jika retry β response cached 24 jam- Callback timeout: jika tidak callback dalam window, EMBAN kirim
callback_timeout_messageke customer
04 Ticketing API (CS Escalation)
Ticketing adalah jalur "forward to human" β ketika agent CS tidak mampu menjawab otomatis, ia membuat ticket dan meneruskannya ke sistem ticketing milik Tenant IT. Petugas manusia membalas, EMBAN merelay balasan ke customer.
4.1 Model
| Istilah | Artinya |
|---|---|
| Ticket | Row di tabel tickets. Format nomor: {prefix}-{yyyymmdd}-{seq6} |
| Ticket message | Trail percakapan. sender_type: customer / agent / tenant_system |
| Webhook URL | Endpoint Tenant IT untuk notifikasi ticket.created |
| Webhook secret | Pre-shared HMAC secret per-tenant |
4.2 Status Lifecycle
open β fwd_to_tenant β awaiting_reply β tenant_replied β closed
β³ reopened (customer balas lagi)
4.3 Outbound β EMBAN → Tenant IT
POST {tenants.ticket_webhook_url}
Event: ticket.created. Skema: ts-prefixed.
{
"event": "ticket.created",
"ticket_id": "01J1ZXK7...",
"ticket_number": "RAI-20260420-000007",
"customer": {
"channel": "whatsapp",
"platform_id": "+628123456789",
"display_name": "Budi Santoso"
},
"subject": "Tidak bisa login ke dashboard",
"initial_message": "Saya sudah reset password tapi tetap error...",
"priority": "normal",
"sla_due_at": "2026-04-20T14:30:00.000Z",
"reply_callback_url": "https://api.rentalai.id/api/v1/tickets/01J.../reply"
}
4.4 Inbound β Tenant IT → EMBAN (Reply)
POST /api/v1/tickets/:id/reply
{
"message": "Silakan coba login ulang, kami sudah reset dari sisi server.",
"close_ticket": false,
"internal_note": false
}
| Field | Tipe | Catatan |
|---|---|---|
message | string (wajib) | Dikirim ke customer di channel asli |
close_ticket | boolean | Set true untuk tutup tiket |
internal_note | boolean | Jika true, tidak dikirim ke customer |
05 Action Engine
Action adalah mekanisme yang membuat customer bisa memicu side-effect di sistem Tenant IT lewat percakapan natural. Contoh: customer ngetik "mau booking Toyota Avanza untuk besok", agent CS mengumpulkan field lalu POST ke endpoint Tenant IT.
5.1 Alur End-to-End
Customer (WhatsApp/TG/Email)
β "Mau booking mobil untuk besok"
βΌ
Agent CS (LLM) β collect fields conversationally
βΌ
EMBAN action-engine β validate β autofill β sign β POST
βΌ
Endpoint Tenant IT
βββ SYNC: response body di-render langsung ke customer
βββ ASYNC: customer dapat "sedang diproses" β Tenant IT callback
5.2 Action Definition (ringkasan)
Action didaftarkan oleh tenant via percakapan EMBAN. Yang perlu Tenant IT tahu: endpoint + auth + response blocks.
{
"name": "book_car",
"description": "Create car rental booking",
"schema_version": "1.0",
"endpoint": {
"url": "https://api.tenant.example/emban/book-car",
"method": "POST",
"auth": { "type": "hmac_sha256", "secret_env": "EMBAN_ACTION_BOOK_CAR" }
},
"response": {
"mode": "async",
"immediate_ack": { "id": "Booking sedang diproses..." },
"error_templates": {
"booking_confirmed": { "id": "Booking {{booking_id}} dikonfirmasi." },
"default": { "id": "Sistem: {{error_message}}" }
}
}
}
5.3 Outbound Request β EMBAN → Tenant IT
POST {action.endpoint.url}
Skema signature: body-only (bukan ts-prefixed).
Headers: X-EMBAN-Signature, X-EMBAN-Tenant-Id, X-EMBAN-Submission-Id, X-EMBAN-Schema-Version, Idempotency-Key.
{
"submission_id": "01J1ZXK7...",
"action_name": "book_car",
"fields": {
"car_model": "avanza",
"pickup_date": "2026-04-22",
"customer_phone": "+628123456789"
},
"customer": {
"channel": "whatsapp",
"platform_id": "+628123456789",
"display_name": "Budi"
}
}
5.4 Response dari Tenant IT
Sync mode β return body langsung:
HTTP 200
{ "status": "success", "template_key": "booking_confirmed",
"data": { "booking_id": "BK-2026-0412" } }
Async mode β return 202 Accepted, lalu callback nanti (lihat Section 3):
HTTP 202
{ "ack": true }
5.5 Structured Mode
Async dan Sync mode me-render template ke customer. Structured mode berbeda β engine pass raw JSON ke consumer tool internal EMBAN (bukan render ke customer).
success_template/error_templatestidak dipakai β consumer tool yang compose user-facing message- Backend WAJIB return JSON dengan shape spesifik per consumer tool
- Gunakan untuk: tenant-internal data lookup (bukan end-customer facing)
Contoh kanonik: refresh_inventory_from_backend + Template Library trio (business_metric, business_list, business_search). Detail di Section 12.
5.6 Retry dari EMBAN
- EMBAN retry maks 3× dengan backoff untuk 5xx/network error
Idempotency-Key=submission_idβ dedup di sisi Tenant IT- 4xx = tidak retry, langsung kirim error template ke customer
06 File Delivery
Tenant IT dapat melampirkan file (invoice PDF, foto unit, dll.) di callback response yang akan dikirim ke customer di channel asli.
6.1 Schema per File
{ "url": "https://files.tenant.example/invoice.pdf",
"filename": "invoice.pdf",
"mime_type": "application/pdf",
"expected_size_bytes": 524288 }
6.2 Aturan & Batasan
| Aturan | Nilai |
|---|---|
| Protokol | HTTPS wajib |
| Hostname | Harus cocok allowlist per-tenant (tenants.file_download_allowlist) |
| Max size per file | 10 MB |
| Max files per callback | 10 |
| MIME allowlist | PDF, PNG, JPG, MP3, MP4, docx, xlsx, zip |
| Content sniff | Magic bytes dicocokkan dengan mime_type |
| TTL di disk EMBAN | 24 jam (auto-delete) |
| Connect timeout | 10 detik |
| Read timeout | 30 detik |
6.3 Partial Success
Text message selalu di-deliver dulu. Setiap file diproses independen β status per file: delivered, rejected (validation), fetch_failed (network), delivery_failed (channel error).
07 Working Hours
Awareness β apa yang terjadi saat customer masuk di luar jam kerja tenant.
7.1 Konfigurasi
Tenant owner mengatur jam kerja via percakapan dengan EMBAN. Default: SeninβJumat 09:00β17:00 di timezone tenant.
7.2 Behavior di Luar Jam Kerja
- Customer tetap bisa chat β agent CS merespons dengan pesan out-of-hours
- Ticket tetap dibuat tapi SLA mulai dihitung dari jam kerja berikutnya
- Action engine tetap fire β endpoint Tenant IT tetap menerima request (backend Anda harus available 24/7 kalau action didaftarkan)
β οΈ Working hours hanya mempengaruhi behavior agent CS (persona, SLA). Endpoint API Anda tetap dipanggil kapan saja β pastikan uptime 24/7 jika action aktif.
08 Error Codes
Konsolidasi semua error_code yang mungkin muncul, plus detail rate limit & idempotency.
8.1 Authentication (HTTP 401)
error_code | Artinya | Aksi |
|---|---|---|
missing_auth_headers | Signature/Timestamp absen | Kirim keduanya |
invalid_timestamp | Bukan angka Unix millis | Kirim string angka desimal |
timestamp_skew | Drift > 5 menit | NTP sync; cek details.drift_ms |
unknown_signer | Secret tidak dikenali | Cek tenant/action sudah ter-setup |
invalid_signature | HMAC mismatch | Cek body bytes, secret, hex lowercase, prefix sha256= |
8.2 Validation (HTTP 400)
error_code | Artinya |
|---|---|
schema_invalid | Body gagal validasi β cek details.fieldErrors |
request_error | Generic client error |
8.3 Not Found (HTTP 404)
error_code | Artinya |
|---|---|
submission_not_found | Submission ID tidak ada / expired |
ticket_not_found | Ticket ID tidak ada |
8.4 Conflict (HTTP 409)
error_code | Artinya | Aksi |
|---|---|---|
invalid_submission_state | Submission bukan awaiting_callback | Jangan retry |
invalid_ticket_state | Ticket sudah closed | Jangan retry |
idempotency_key_conflict | Key sama dipakai endpoint berbeda | Ganti key |
8.5 Rate Limit (HTTP 429)
error_code | Artinya | Aksi |
|---|---|---|
rate_limit_exceeded | Kuota habis (default 60 req/menit) | Tunggu details.retry_after_ms |
8.6 Server Error (HTTP 5xx)
error_code | Aksi |
|---|---|
action_missing / action_definition_corrupt | Escalate ke tim EMBAN dengan request_id |
no_customer_linked / no_agent_for_tenant | State rusak β escalate |
delivery_unavailable / delivery_failed | Retry dengan backoff; jika konsisten β escalate |
8.7 Idempotency
- Semua endpoint write menerima
Idempotency-Key(UUID/ULID) - Cache 24 jam β retry dengan key sama mengembalikan response cached
- Key < 128 karakter, unique per logical operation
09 Contoh End-to-End
Flow lengkap yang merangkai endpoint-endpoint individual. Kode contoh pakai Node.js β Python/curl setara ada di doc endpoint masing-masing.
Contoh 1 β Customer Booking (Async Action)
Skenario: Customer WA "Mau booking Avanza untuk hari Rabu." Agent CS kumpulkan field, fire action.
- EMBAN → Tenant IT:
POST https://backend.tenant.com/emban/book(body-only HMAC) - Tenant IT: Terima, return
202 { "ack": true } - EMBAN → Customer: "Booking sedang diproses, mohon tunggu..."
- Tenant IT → EMBAN:
POST /api/v1/callbacks/:submission_id(ts-prefixed HMAC) - EMBAN → Customer: "Booking BK-2026-0412 untuk Toyota Avanza dikonfirmasi."
Contoh 2 β Ticket Escalation (CS Forward to Human)
- Customer: "Saya mau komplain, pesanan salah."
- Agent CS: Tidak bisa handle → buat ticket
- EMBAN → Tenant IT:
POST {webhook_url}eventticket.created - Tenant IT operator: Review ticket, kirim balasan
- Tenant IT → EMBAN:
POST /api/v1/tickets/:id/reply - EMBAN → Customer: Relay balasan operator
- Tenant IT → EMBAN: Reply lagi dengan
close_ticket: true
Contoh 3 β Sync Action (Cek Stok)
- Customer: "Apakah Innova masih ada untuk tanggal 25?"
- EMBAN → Tenant IT:
POST {action_url} - Tenant IT: Return
200langsung dengan availability data - EMBAN → Customer: Render template dengan data response
10 Glossary
Aktor
| Istilah | Definisi |
|---|---|
| Tenant | Pelanggan yang subscribe plan EMBAN. Bicara via WA/TG/Email, bukan API. |
| Tenant owner | Manusia pemilik akun Tenant. Set konfigurasi via chat dengan agent. |
| Tenant IT | Tim developer di sisi Tenant yang membangun backend integrasi. Audience utama doc ini. |
| Customer | End-user yang dilayani agent CS. Pelanggan dari Tenant. |
| Agent | Engine LLM per-tenant yang menjawab message. |
| Upstream | Pihak yang menyewakan EMBAN ke tenant (provisioning). Tidak relevan dengan tugas Anda. |
Plan & Role
| Istilah | Definisi |
|---|---|
| Plan | Tier billing: basic, pro, cs |
| Role | Tipe agent: personal_assistant, customer_service, store_admin. Tidak bisa di-switch setelah create. |
Teknis
| Istilah | Definisi |
|---|---|
| Action | Custom side-effect yang agent bisa trigger ke endpoint Tenant IT |
| Submission | Satu instansi eksekusi action (ULID). Lifecycle: collecting → submitted → awaiting_callback → callback_delivered |
| Ticket | Eskalasi dari agent CS ke human. Row di tabel tickets |
| Channel | Platform komunikasi: whatsapp, telegram, email |
| HMAC | Hash-based Message Authentication Code (SHA-256) |
| Idempotency-Key | Header untuk mencegah duplikasi. Cache 24 jam. |
11 Reference Implementation
Kode produksi di repo yang sudah mengimplementasi pola receiver standar. Anda bisa study code dan mirror struktur untuk backend Anda sendiri.
11.1 Komponen yang Di-implement
| Komponen | Spec Doc | Deskripsi |
|---|---|---|
| Ticket webhook receiver | #4 §3 | Menerima ticket.created, verifikasi HMAC, simpan ke DB |
| Admin dashboard tickets | Internal | List + reply ticket via UI |
| Product query stub | #5 | Contoh action endpoint sync |
11.2 HMAC Patterns
- Verify inbound (webhook dari EMBAN): ts-prefixed,
express.raw(),timingSafeEqual - Sign outbound (reply ke EMBAN): ts-prefixed, body di-stringify sekali lalu dikirim
- Verify action (outbound dari EMBAN): body-only, tanpa timestamp
11.3 Environment Variables
# Ticket webhook
EMBAN_TICKET_WEBHOOK_SECRET=your-ticket-secret-here
EMBAN_API_BASE_URL=https://api.rentalai.id
# Action endpoint (per action)
EMBAN_ACTION_SECRET_BOOK_CAR=your-action-secret-here
11.4 Smoke Test Checklist
- Health check:
GET /api/v1/healthreturns200 - Ticket webhook: verify HMAC → store → return
200 - Ticket reply: sign → POST → verify
200 ok:true - Action sync: verify body-only HMAC → return
200dengan data - Action async: return
202→ callback → verify delivery
12 Template Library (Business-Data Trio)
Library dari 3 generic action template untuk read-only query data backend. Owner register action JSON yang sudah disediakan, Tenant IT implement satu endpoint per template β dispatch internal berdasarkan field. Tidak perlu custom-define action per-query.
π‘ Kenapa template library? Praktek lapangan: tenant register 1 custom action per query (metric_orders_today, metric_revenue_mtd, ...) skalanya jelek. Lebih bersih: 1 endpoint backend menerima dispatch key, internal switch ke handler per-metric.
12.1 Kapan Pakai Template Library
| Use Case | Template | Contoh |
|---|---|---|
| Single-number metric (count/sum/avg/persen) | business_metric | "Berapa order hari ini?" |
| Paginated list dari resource | business_list | "List 20 client terbaru" |
| Free-text search di resource | business_search | "Cari produk kemeja batik" |
β οΈ Jangan pakai template library untuk: side-effect write (create/update/delete), multi-step workflow, atau sensitive data yang tidak boleh masuk LLM hint.
12.2 Pola Umum
- Response mode:
structuredβ EMBAN engine TIDAK render template, consumer tool compose user-facing message dari JSON mentah - HMAC: Body-only scheme (sama dengan Action Engine Β§5). Convention: 1 secret per template
- Register: Owner panggil
register_tenant_actiondi chat, paste JSON template
12.3 Template 1 β business_metric
POST {endpoint}/emban/business-metric
Single-number query. Dispatch berdasarkan metric_name.
Request body (dari EMBAN):
{ "data": { "metric_name": "orders_today", "filter_period": "today" } }
Response contract (wajib dari backend):
{
"metric_name": "orders_today",
"value": 42,
"value_type": "count",
"label": "Total order hari ini",
"filter_period": "today",
"as_of": "2026-05-13T10:00:00+07:00"
}
| Field | Wajib | Catatan |
|---|---|---|
value | Ya | Finite number |
value_type | Ya | count | currency | percentage | duration_seconds |
label | Ya | Human-readable framing |
metric_name | Opsional | Echo back dispatch key |
filter_period | Opsional | Echo back filter |
as_of | Opsional | ISO 8601 timestamp data |
12.4 Template 2 β business_list
POST {endpoint}/emban/business-list
Paginated list dari resource. Dispatch berdasarkan resource.
Request body:
{ "data": { "resource": "clients", "page": 1, "page_size": 20,
"sort_by": "created_at", "sort_order": "desc",
"filter_status": "active" } }
Response contract:
{
"resource": "clients",
"items": [
{ "id": "C001", "name": "PT Maju Jaya", "status": "active", ... }
],
"total": 150,
"page": 1,
"page_size": 20,
"columns": ["id", "name", "status", "created_at"]
}
| Field | Wajib | Catatan |
|---|---|---|
items | Ya | Array of objects (max 50 per page) |
total | Ya | Total count (untuk info "halaman X dari Y") |
page, page_size | Ya | Echo back pagination |
columns | Opsional | Urutan kolom untuk display |
resource | Opsional | Echo back resource name |
12.5 Template 3 β business_search
POST {endpoint}/emban/business-search
Free-text search di resource. Dispatch berdasarkan resource + query.
Request body:
{ "data": { "resource": "products", "query": "kemeja batik",
"page": 1, "page_size": 10 } }
Response contract:
{
"resource": "products",
"query": "kemeja batik",
"items": [
{ "id": "P042", "name": "Kemeja Batik Pria Slim", "price": 189000, ... }
],
"total": 3,
"page": 1,
"page_size": 10,
"columns": ["id", "name", "price", "stock"]
}
12.6 Error Code Mapping
| HTTP | error_code | Artinya |
|---|---|---|
| 400 | unknown_metric / unknown_resource | Dispatch key tidak dikenali backend |
| 400 | invalid_filter | Filter parameter tidak valid |
| 401 | invalid_signature | HMAC gagal (lihat Section 2) |
| 500 | internal_error | Backend error β consumer tampilkan "data tidak tersedia saat ini" |
12.7 FAQ
- Boleh skip HMAC? Tidak. Structured mode tetap di-verify HMAC oleh engine.
- Backend beda bahasa (PHP/Python/Go)? Bisa β contract adalah JSON over HTTP, language-agnostic. Contoh Node.js hanya sebagai referensi.
- Boleh return field tambahan? Ya, tapi consumer tool akan mengabaikan field yang tidak ada di contract.
- Backend lambat >30 detik? Template library designed for <8 detik sync. Kalau lambat, pakai custom action async mode instead.
- Anti-pattern: 1 action per metric. Jangan register
metric_orders_today,metric_revenue_mtd, dll sebagai action terpisah. Pakai 1business_metric+ dispatch internal berdasarkanmetric_name.