Docs
Reference lengkap buat KOL: dari signup sampai signal fanout end-to-end. Setiap section punya example konkret + edge case + troubleshooting note.
Quickstart
3 menit dari nol sampai signal pertama ke-fanout. TL;DR — selebihnya di section bawah.
- 1Signup: /pricing → pilih tier → login Telegram → isi tenant name + slug.
- 2Bot: @BotFather →
/newbot→ copy token → di-paste ke/admin/tenant-bots. Lalu/setprivacy → Disable. - 3Invite bot ke grup VIP / channel lo. Bot otomatis DM lo chat ID begitu join.
- 4Register channel di
/admin/channels→ pilih parser → enabled. - 5Test: kirim sample signal di grup. Cek
/admin/signals— appear sebagai parsed + fanout fired ke subscribers.
Konsep & arsitektur
Pahamin model di belakang layar biar setup-nya jelas.
kopitrade itu multi-tenant infrastructure-as-a-service buat sinyal trading. Lo (KOL) bawa tiga hal:
- Bot Telegram sendiri (dari BotFather)
- Channel / grup tempat lo post sinyal
- Subscriber base lo sendiri (lo collect fee, kita gak masuk)
Kita orchestrate:
- Webhook routing — message dari bot lo masuk ke pipeline kita
- Parser plugins — convert raw text → structured signal (pair, side, entry, TPs, SL)
- Fanout queue — 1 signal × N subscribers (BullMQ + Redis)
- Order placement — execute di exchange subscriber via stored credentials (AES-256-GCM at-rest)
- Tenant admin panel — channel management + signal feed + billing
KOL onboarding
Step-by-step dari signup sampai bot ready.
3.1 · Tenant signup
Buka /pricing, pilih tier, klik Mulai trial.
Login pakai Telegram deeplink (gak ada password — bot DM lo magic link 5-menit sekali pakai).
Isi form tenant:
- Tenant name — display, e.g. "Liquidity Waves VIP"
- Slug — subdomain lo:
<slug>.kopitrade.id - Plan — Starter / Growth / Pro
3.2 · Bot setup
Di Telegram, chat dengan @BotFather:
/newbot > Bot display name? Liquidity Waves Signals > Bot username? lws_signals_bot (must end with _bot) # BotFather replies with token like: # 1234567890:ABCdefGHIjklMNOpqrSTUvwxYZ-0123456789 /setprivacy > Choose your bot: lws_signals_bot > 'Disable'
/setprivacy → Disable. Default-nya privacy ON, artinya bot CUMA baca pesan yang mention bot atau reply ke bot. Untuk capture semua signal di grup, harus disable.Copy token dari BotFather → buka kopitrade.id/admin/tenant-bots → paste → klik Verify + Register.
Kita auto-do:
- Verify via
getMe— token valid + bot username + id captured - Encrypt token (AES-256-GCM) + simpan ke
Tenant.botTokenEnc - Generate random webhook secret (24 byte base64url)
- Call
setWebhookkehttps://kopitrade.id/api/tg/webhook/<secret>
3.3 · Invite bot ke grup
Buka grup VIP / channel lo, klik member list → Add → cari username bot lo → Add.
| Chat type | Bot role needed | Notes |
|---|---|---|
| supergroup (VIP grup) | member regular | privacy must be disabled |
| channel (broadcast) | admin (Post Messages) | channel post events only |
| basic group | member regular | jarang dipake VIP (max 200 member) |
Bot otomatis DM chat owner begitu di-invite ke chat baru:
☕ Bot bergabung di chat baru Chat: Liquidity Waves VIP (@liquidity_waves) Type: supergroup Chat ID: -1001234567890 Buka /admin/channels buat register chat ini sebagai listener source.
3.4 · Register channel
Buka kopitrade.id/admin/channels → klik Add channel:
- Telegram chat ID — paste dari DM bot (bisa negatif untuk channel/supergroup)
- Display name — friendly label, e.g. "LW VIP"
- Handle (optional) — @username channel
- Parser plugin — pilih format yang match dengan style sinyal lo (lihat section 4)
Klik Add channel → status default enabled, listener langsung start route message.
Parser plugins
Tiap channel di-bind ke satu parser. Pilih yang paling match formatnya — bisa di-test di sandbox sebelum go-live.
/admin/parsers punya interactive sandbox — paste sample signal, lihat hasil parse + confidence score.4.1 · regex.basic
Universal pattern-matching. Cocok buat channel yang format-nya simpel + konsisten.
Cara kerja: regex pass dua kali — first try strict pair pattern (e.g. "BTC/USDT", "BTCUSDT"), fallback ke bare ticker + side keyword.
Confidence: 1.0 kalo semua field ter-extract clean, turun proportional kalo ada yang missing.
Min confidence default: 0.7
4.2 · cornix-format
Specific buat KOL yang lagi migrate dari Cornix — recognize Cornix-style emoji + label patterns (🟢 Long, Entry:, TP:, Stop:).
Strict: kalo format gak Cornix, reject dengan reason not a Cornix-format message. Pakai bareng channel yang KOL post pakai Cornix template.
Min confidence default: 0.85 (high — format strict + predictable)
4.3 · liquidity-waves
Format LIQUIDITY WAVES SETUP — strict header check.
Detection: mengandung string "LIQUIDITY WAVES SETUP" (case-insensitive) di awal pesan. Lalu parse hashtag-pair, side keyword, Entry range, TP1-N, SL line.
Min confidence default: 0.85
4.4 · ai.fallback
Claude Haiku-powered, last-resort. Pakai kalau channel lo format-nya custom / berubah-ubah / gak match regex pattern.
Hallucination guard: setiap angka yang AI extract harus appear verbatim di source text. Kalau AI generate angka yang gak ada di message asli, parse di-reject.
Cost: ~$0.001 per signal. Kita absorb cost untuk Growth tier (limited rate), Pro tier unlimited.
Min confidence default: 0.75
Signal format · sample yang work
Format-format yang ter-verify parse clean. Lo bisa mix style ini di channel lo.
5.1 · LIQUIDITY WAVES format
LIQUIDITY WAVES SETUP #BTCUSDT LONG x10 Entry: 67200 — 67400 TP1: 68100 TP2: 68900 TP3: 70200 SL: 66500
{
"pair": "BTC",
"side": "long",
"entry": [67200, 67400],
"tps": [68100, 68900, 70200],
"sl": 66500,
"leverage": 10
}
confidence: 0.955.2 · Cornix-style format
🟢 Long #ETHUSDT Entry: 3850 TP: 3900, 3950, 4020 Stop: 3780 Leverage 5x
{
"pair": "ETH",
"side": "long",
"entry": [3850, 3850],
"tps": [3900, 3950, 4020],
"sl": 3780,
"leverage": 5
}
confidence: 0.925.3 · Bare regex format
#SOLUSDT LONG x5 Entry: 168 - 172 TP1: 178 TP2: 185 SL: 162
{
"pair": "SOL",
"side": "long",
"entry": [168, 172],
"tps": [178, 185],
"sl": 162,
"leverage": 5
}
confidence: 1.0# atau bare. Quote currency (USDT / USDC) optional. Side keyword (LONG/SHORT/BUY/SELL) case-insensitive.Subscriber flow
Dari sisi end-user yang follow signal lo.
Subscriber daftar di kopitrade.id/login (Telegram deeplink), connect exchange mereka sendiri (Binance / Bybit / OKX / KuCoin / Bitget), lalu subscribe ke tenant lo dari /me/signals.
Setiap signal yang masuk ke channel lo dengan confidence ≥ floor:
- 1Worker resolve siapa aja yang subscribe active ke tenant lo
- 2Tiap subscriber → enqueue order.place job (default paperTrade=true)
- 3Worker decrypt cred → call adapter → place order
- 4JobAudit row written buat post-mortem visibility
Exchange integration
5 tier-1 native, 90+ via CCXT fallback.
| Exchange | Tier | Capabilities | Notes |
|---|---|---|---|
| Binance | 1 | Spot · Futures · Margin | Global liquidity, lowest spread |
| Bybit | 1 | Spot · Futures · Copy | ID-friendly, popular di SEA |
| OKX | 1 | Spot · Futures | Passphrase required |
| KuCoin | 1 | Spot · Futures | Passphrase required |
| Bitget | 1 | Copy-trading native | Copy-trade-friendly |
| BingX, MEXC, HTX, BloFin, … | 2 (CCXT) | Spot · Futures (limited) | Capability flags inferred |
Permission API-key (untuk subscriber)
- Enable: Trading + Read
- Disable: Withdraw (CRITICAL — jangan pernah enable)
- IP whitelist: kontak support buat dapet IP VPS kopitrade kalau exchange-nya support
Billing & invoicing
Pakasir (QRIS-native IDR processor). Pay-as-you-go, opt-IN renewal default.
Flow:
- 1Tenant admin buka
/t/<slug>/billing→ pilih plan →Bayar - 2Kita create Payment row + redirect ke Pakasir hosted page
- 3Subscriber scan QRIS, complete payment
- 4Pakasir webhook ke
kopitrade.id/api/payments/pakasir/webhookdenganx-webhook-secretheader - 5Kita verify secret + amount + order_id, lalu activate subscription (extend period 1 bulan dari periodEnd existing atau dari now)
- 6Tenant
pending → activekalau ini bayar pertama
autoRenew=false. Kita gak auto-charge tanpa permission eksplisit dari lo. Lo manually re-checkout sebelum periodEnd.Refund policy: trial 14-hari = full refund kalau cancel sebelum trial end. Setelah bayar pertama, prorated refund untuk unused days.
Webhook API reference
Endpoint Telegram bot kita expose buat receive updates dari tenant bot.
9.1 · Telegram bot webhook
Endpoint: POST /api/tg/webhook/<secret>
Auth: URL path <secret> matches Tenant.webhookSecret AND X-Telegram-Bot-Api-Secret-Token header matches.
Handled updates:
['message', 'edited_message', 'channel_post', 'edited_channel_post', 'my_chat_member']
Response: always 200 OK (intentional — prevent Telegram retry storms; errors logged internally).
9.2 · Pakasir billing webhook
Endpoint: POST /api/payments/pakasir/webhook
Auth: X-Webhook-Secret header matches PAKASIR_WEBHOOK_SECRET env, ATAU source IP di PAKASIR_WEBHOOK_IPS allowlist.
Payload shape:
{
"project": "kopitrade-indonesia",
"order_id": "tenant-slug-plan-abc123",
"amount": 1990000,
"status": "completed",
"payment_method": "qris"
}Idempotency: by order_id — already-paid Payment returns {ok:true, idempotent:true}.
9.3 · Manual signal ingest (admin only)
Endpoint: POST /api/admin/signals/ingest
Auth: admin cookie (kopi_admin) required.
Request:
{
"channelId": "cmpesysur0001m2f7lag7rjrr",
"rawText": "#BTC LONG\nEntry: 67200\nTP1: 68100\nSL: 66500",
"fakeMessageId": 12345
}Use case: demo/testing pipeline tanpa bot live, atau replay/backfill missed signals.
Troubleshooting
Common issue + fix.
Bot udah join grup, tapi gak ada signal ke-capture
- 1Cek
/setprivacybot di BotFather — harus Disable. Default Enable = bot cuma baca mention/reply. - 2Cek bot status di
/admin/channels— channel harusenabled, bukanpaused. - 3Cek
Tenant.lastWebhookAt— kalau null / quiet >24h, webhook canary bakal alert. Re-register bot via/admin/tenant-botskalau token expired.
Signal ke-capture tapi status 'review' / 'rejected'
Confidence di bawah parser plugin minimum. Cek di /admin/signals → klik raw text → lihat reject reason.
Fix options:
- Pakai parser yang lebih strict-match format lo (test di
/admin/parserssandbox) - Pro tier: train custom AI parser ke format spesifik
- Update format signal lo biar match regex pattern existing
Pakasir bayar udah masuk tapi tenant masih 'pending'
Webhook gak fire / di-reject. Cek:
- Pakasir dashboard: webhook URL ke
https://kopitrade.id/api/payments/pakasir/webhookset? - Webhook secret di Pakasir dashboard match dengan
PAKASIR_WEBHOOK_SECRETenv kita? - Payment row di DB ada dengan status
pending? Kalau ada, hit webhook ulang dari Pakasir dashboard (retry button).
Kontak support kalau persistent — kita bisa manually mark paid + activate.
Subscriber gak dapet order placement dari signal gw
Cek:
- Subscriber subscribe ke tenant lo + status
active? - Subscriber connect minimal 1 exchange dengan status
ok? - Signal status di
/admin/signalsfanned-out? - JobAudit row di DB success? Kalau failed, error column kasih clue.
Forward signal dari channel lain ke grup VIP — auto-execute lagi?
Tidak. Webhook punya forward guard: setiap message dengan forward_origin / forward_from / forward_from_chat di-skip silently. Mencegah replay attack + dedup issue.
Glossary
Istilah teknis yang muncul di docs + dashboard.
- Tenant
- Lo, sebagai KOL. Tiap KOL = 1 tenant. Memiliki bot + channel + subscriber feed sendiri.
- Subscriber
- User end-customer yang follow signal lo + bayar lo. Connect exchange API key sendiri.
- Channel (listener)
- Telegram chat (grup / supergroup / channel) yang lo set sebagai sumber signal. Mapped 1-to-1 dengan ParserPlugin.
- ParserPlugin
- Code yang convert raw text → ParsedSignal. Built-in: regex.basic, cornix-format, liquidity-waves, ai.fallback.
- SignalEvent
- DB row per message yang lewat parser. Status:
received→parsed→fanned-out(ataureview/rejected). - Fanout
- Proses 1 signal × N subscriber → N order.place job. Powered by BullMQ + Redis.
- Webhook secret
- Random 24-byte token di URL
/api/tg/webhook/<secret>; harus match Tenant row +X-Telegram-Bot-Api-Secret-Tokenheader. Defense-in-depth. - Paper-trade
- Order simulasi — adapter returns synthetic
paper:<ts>:<rand>id. Phase 2 default, safety net pra-live-trading. - JobAudit
- DB mirror dari BullMQ jobs. Setiap job lifecycle (running → completed / failed) di-record. Buat post-mortem visibility.
- RLS
- Row Level Security — PostgreSQL policy yang scope DB query by session GUC. Memastikan user-A gak bisa baca / write row user-B walaupun app-layer bug.