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 cs dan 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.created dari 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_id untuk action mode asinkron.

1.2 Aktor & Boundary

AktorSiapaBicara ke EMBAN lewat
Tenant IT (Anda)Developer di sisi tenant CSHTTP API callback/reply + webhook receiver, HMAC per-tenant/per-action
Tenant ownerEnd-user yang bayar planWhatsApp / Telegram / Email β€” bukan API
CustomerPelanggan akhir dari tenant CSWhatsApp / Telegram / Email, direlay lewat tenant
EMBAN enginePusat orchestrationMenerima 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)

SecretSkupDipakai di
ticket_webhook_secrettenant-scoped, 1 per tenantOutbound ticket.created webhook + inbound /api/v1/tickets/:id/reply
action.secretaction-scoped, N per tenantOutbound 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 LogisScopeDipakai di
Ticket webhook secretper-tenantOutbound ticket.created webhook + inbound POST /api/v1/tickets/:id/reply
Action callback secretper-action, per-tenantOutbound 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:

HeaderNilai
X-EMBAN-TimestampUnix millis (ts-prefixed saja)
X-EMBAN-Signaturesha256=<hex-digest>
Content-Typeapplication/json
Idempotency-KeyUUID/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

HTTPerror_codeArtinya
401missing_auth_headersHeader signature/timestamp absen
401invalid_timestampTimestamp bukan angka
401timestamp_skewDrift > 5 menit β€” sync NTP
401unknown_signerSecret tidak dikenali untuk tenant ini
401invalid_signatureSignature 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

IstilahArtinya
ActionSpesifikasi kustom tenant: endpoint + fields + templates. Row di tenant_actions.
SubmissionSatu instansi eksekusi action. Status: collectingsubmittedawaiting_callbackcallback_delivered.
Sync modeEMBAN tunggu response HTTP langsung. Tidak ada callback.
Async modeEMBAN balas immediate_ack, lalu tunggu callback dari Tenant IT.

3.2 Endpoint

POST /api/v1/callbacks/:submission_id

  • submission_id diberikan di outbound request pertama (header X-EMBAN-Submission-Id)
  • Secret untuk HMAC = value dari action.endpoint.auth.secret_env
  • Skema: ts-prefixed

3.3 Request Body

FieldTipeWajibCatatan
status"success" | "error"default "success"Menentukan template
template_keystringoptionalOverride eksplisit nama template
dataobjectdefault {}Payload untuk interpolasi template
error_codestringoptionalDicari di error_templates
error_messagestringoptionalTersedia sebagai {{error_message}}
filesarray ≤ 10optionalAttachment 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 }
AturanNilai
ProtokolHTTPS wajib
HostnameHarus cocok allowlist per-tenant
Max size per file10 MB
Max files per callback10
MIME allowlistPDF, PNG, JPG, MP3, MP4, docx, xlsx, zip
TTL di disk EMBAN24 jam (auto-delete)

3.6 Error Codes

HTTPerror_codeAksi
400schema_invalidPerbaiki payload
404submission_not_foundCek submission_id
409invalid_submission_stateJangan retry β€” state tidak akan balik
503delivery_unavailableRetry dengan backoff

3.7 Retry & Idempotency

  • Retry maks 4× dengan backoff 2s/4s/8s/16s untuk 5xx
  • Idempotency-Key wajib jika retry β€” response cached 24 jam
  • Callback timeout: jika tidak callback dalam window, EMBAN kirim callback_timeout_message ke 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

IstilahArtinya
TicketRow di tabel tickets. Format nomor: {prefix}-{yyyymmdd}-{seq6}
Ticket messageTrail percakapan. sender_type: customer / agent / tenant_system
Webhook URLEndpoint Tenant IT untuk notifikasi ticket.created
Webhook secretPre-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
}
FieldTipeCatatan
messagestring (wajib)Dikirim ke customer di channel asli
close_ticketbooleanSet true untuk tutup tiket
internal_notebooleanJika 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_templates tidak 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

AturanNilai
ProtokolHTTPS wajib
HostnameHarus cocok allowlist per-tenant (tenants.file_download_allowlist)
Max size per file10 MB
Max files per callback10
MIME allowlistPDF, PNG, JPG, MP3, MP4, docx, xlsx, zip
Content sniffMagic bytes dicocokkan dengan mime_type
TTL di disk EMBAN24 jam (auto-delete)
Connect timeout10 detik
Read timeout30 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_codeArtinyaAksi
missing_auth_headersSignature/Timestamp absenKirim keduanya
invalid_timestampBukan angka Unix millisKirim string angka desimal
timestamp_skewDrift > 5 menitNTP sync; cek details.drift_ms
unknown_signerSecret tidak dikenaliCek tenant/action sudah ter-setup
invalid_signatureHMAC mismatchCek body bytes, secret, hex lowercase, prefix sha256=

8.2 Validation (HTTP 400)

error_codeArtinya
schema_invalidBody gagal validasi β€” cek details.fieldErrors
request_errorGeneric client error

8.3 Not Found (HTTP 404)

error_codeArtinya
submission_not_foundSubmission ID tidak ada / expired
ticket_not_foundTicket ID tidak ada

8.4 Conflict (HTTP 409)

error_codeArtinyaAksi
invalid_submission_stateSubmission bukan awaiting_callbackJangan retry
invalid_ticket_stateTicket sudah closedJangan retry
idempotency_key_conflictKey sama dipakai endpoint berbedaGanti key

8.5 Rate Limit (HTTP 429)

error_codeArtinyaAksi
rate_limit_exceededKuota habis (default 60 req/menit)Tunggu details.retry_after_ms

8.6 Server Error (HTTP 5xx)

error_codeAksi
action_missing / action_definition_corruptEscalate ke tim EMBAN dengan request_id
no_customer_linked / no_agent_for_tenantState rusak β€” escalate
delivery_unavailable / delivery_failedRetry 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.

  1. EMBAN → Tenant IT: POST https://backend.tenant.com/emban/book (body-only HMAC)
  2. Tenant IT: Terima, return 202 { "ack": true }
  3. EMBAN → Customer: "Booking sedang diproses, mohon tunggu..."
  4. Tenant IT → EMBAN: POST /api/v1/callbacks/:submission_id (ts-prefixed HMAC)
  5. EMBAN → Customer: "Booking BK-2026-0412 untuk Toyota Avanza dikonfirmasi."

Contoh 2 β€” Ticket Escalation (CS Forward to Human)

  1. Customer: "Saya mau komplain, pesanan salah."
  2. Agent CS: Tidak bisa handle → buat ticket
  3. EMBAN → Tenant IT: POST {webhook_url} event ticket.created
  4. Tenant IT operator: Review ticket, kirim balasan
  5. Tenant IT → EMBAN: POST /api/v1/tickets/:id/reply
  6. EMBAN → Customer: Relay balasan operator
  7. Tenant IT → EMBAN: Reply lagi dengan close_ticket: true

Contoh 3 β€” Sync Action (Cek Stok)

  1. Customer: "Apakah Innova masih ada untuk tanggal 25?"
  2. EMBAN → Tenant IT: POST {action_url}
  3. Tenant IT: Return 200 langsung dengan availability data
  4. EMBAN → Customer: Render template dengan data response

10 Glossary

Aktor

IstilahDefinisi
TenantPelanggan yang subscribe plan EMBAN. Bicara via WA/TG/Email, bukan API.
Tenant ownerManusia pemilik akun Tenant. Set konfigurasi via chat dengan agent.
Tenant ITTim developer di sisi Tenant yang membangun backend integrasi. Audience utama doc ini.
CustomerEnd-user yang dilayani agent CS. Pelanggan dari Tenant.
AgentEngine LLM per-tenant yang menjawab message.
UpstreamPihak yang menyewakan EMBAN ke tenant (provisioning). Tidak relevan dengan tugas Anda.

Plan & Role

IstilahDefinisi
PlanTier billing: basic, pro, cs
RoleTipe agent: personal_assistant, customer_service, store_admin. Tidak bisa di-switch setelah create.

Teknis

IstilahDefinisi
ActionCustom side-effect yang agent bisa trigger ke endpoint Tenant IT
SubmissionSatu instansi eksekusi action (ULID). Lifecycle: collecting → submitted → awaiting_callback → callback_delivered
TicketEskalasi dari agent CS ke human. Row di tabel tickets
ChannelPlatform komunikasi: whatsapp, telegram, email
HMACHash-based Message Authentication Code (SHA-256)
Idempotency-KeyHeader 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

KomponenSpec DocDeskripsi
Ticket webhook receiver#4 §3Menerima ticket.created, verifikasi HMAC, simpan ke DB
Admin dashboard ticketsInternalList + reply ticket via UI
Product query stub#5Contoh 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/health returns 200
  • Ticket webhook: verify HMAC → store → return 200
  • Ticket reply: sign → POST → verify 200 ok:true
  • Action sync: verify body-only HMAC → return 200 dengan 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 CaseTemplateContoh
Single-number metric (count/sum/avg/persen)business_metric"Berapa order hari ini?"
Paginated list dari resourcebusiness_list"List 20 client terbaru"
Free-text search di resourcebusiness_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_action di 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"
}
FieldWajibCatatan
valueYaFinite number
value_typeYacount | currency | percentage | duration_seconds
labelYaHuman-readable framing
metric_nameOpsionalEcho back dispatch key
filter_periodOpsionalEcho back filter
as_ofOpsionalISO 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"]
}
FieldWajibCatatan
itemsYaArray of objects (max 50 per page)
totalYaTotal count (untuk info "halaman X dari Y")
page, page_sizeYaEcho back pagination
columnsOpsionalUrutan kolom untuk display
resourceOpsionalEcho 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

HTTPerror_codeArtinya
400unknown_metric / unknown_resourceDispatch key tidak dikenali backend
400invalid_filterFilter parameter tidak valid
401invalid_signatureHMAC gagal (lihat Section 2)
500internal_errorBackend 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 1 business_metric + dispatch internal berdasarkan metric_name.