Documentation

Docs

Reference lengkap buat KOL: dari signup sampai signal fanout end-to-end. Setiap section punya example konkret + edge case + troubleshooting note.

Section 1

Quickstart

3 menit dari nol sampai signal pertama ke-fanout. TL;DR — selebihnya di section bawah.

  1. 1
    Signup: /pricing → pilih tier → login Telegram → isi tenant name + slug.
  2. 2
    Bot: @BotFather → /newbot → copy token → di-paste ke /admin/tenant-bots. Lalu /setprivacy → Disable.
  3. 3
    Invite bot ke grup VIP / channel lo. Bot otomatis DM lo chat ID begitu join.
  4. 4
    Register channel di /admin/channels → pilih parser → enabled.
  5. 5
    Test: kirim sample signal di grup. Cek /admin/signals — appear sebagai parsed + fanout fired ke subscribers.
Trial 14 hari otomatis aktif begitu signup — gak perlu masukin credit card. Lo bisa cancel sebelum hari ke-14 zero charge.
Section 2

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
i
Flow ringkas: Telegram → Bot → Webhook (kita) → Parser → SignalEvent (DB) → Fanout queue → Order placement per subscriber → Exchange.
Section 3

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
Slug final setelah submit — kontak admin kalo perlu ubah (rebrand). Pilih hati-hati.

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'
WAJIB /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 setWebhook ke https://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 typeBot role neededNotes
supergroup (VIP grup)member regularprivacy must be disabled
channel (broadcast)admin (Post Messages)channel post events only
basic groupmember regularjarang 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.

Section 4

Parser plugins

Tiap channel di-bind ke satu parser. Pilih yang paling match formatnya — bisa di-test di sandbox sebelum go-live.

Test parser sebelum register channel: /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

AI fallback cuma fire kalau regex parser fail. Setup channel pakai regex-based parser primary, ai.fallback sebagai backup di config tier yang support.
Section 5

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.95

5.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.92

5.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
Pair bisa di-prefix # atau bare. Quote currency (USDT / USDC) optional. Side keyword (LONG/SHORT/BUY/SELL) case-insensitive.
Section 6

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:

  1. 1
    Worker resolve siapa aja yang subscribe active ke tenant lo
  2. 2
    Tiap subscriber → enqueue order.place job (default paperTrade=true)
  3. 3
    Worker decrypt cred → call adapter → place order
  4. 4
    JobAudit row written buat post-mortem visibility
i
Paper-trade default untuk Phase 2 — buat safety. Live mode toggle ada di subscriber settings (Phase 2.5).
Section 7

Exchange integration

5 tier-1 native, 90+ via CCXT fallback.

ExchangeTierCapabilitiesNotes
Binance1Spot · Futures · MarginGlobal liquidity, lowest spread
Bybit1Spot · Futures · CopyID-friendly, popular di SEA
OKX1Spot · FuturesPassphrase required
KuCoin1Spot · FuturesPassphrase required
Bitget1Copy-trading nativeCopy-trade-friendly
BingX, MEXC, HTX, BloFin, …2 (CCXT)Spot · Futures (limited)Capability flags inferred
Subscriber connect API-key sendiri ke exchange — kita gak custodial. API key di-encrypt AES-256-GCM, plaintext gak pernah masuk DB.

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
Section 8

Billing & invoicing

Pakasir (QRIS-native IDR processor). Pay-as-you-go, opt-IN renewal default.

Flow:

  1. 1
    Tenant admin buka /t/<slug>/billing → pilih plan → Bayar
  2. 2
    Kita create Payment row + redirect ke Pakasir hosted page
  3. 3
    Subscriber scan QRIS, complete payment
  4. 4
    Pakasir webhook ke kopitrade.id/api/payments/pakasir/webhook dengan x-webhook-secret header
  5. 5
    Kita verify secret + amount + order_id, lalu activate subscription (extend period 1 bulan dari periodEnd existing atau dari now)
  6. 6
    Tenant pending → active kalau ini bayar pertama
Opt-IN renewal: default 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.

Section 9

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.

Section 10

Troubleshooting

Common issue + fix.

Bot udah join grup, tapi gak ada signal ke-capture
  1. 1
    Cek /setprivacy bot di BotFather — harus Disable. Default Enable = bot cuma baca mention/reply.
  2. 2
    Cek bot status di /admin/channels — channel harus enabled, bukan paused.
  3. 3
    Cek Tenant.lastWebhookAt — kalau null / quiet >24h, webhook canary bakal alert. Re-register bot via /admin/tenant-bots kalau 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/parsers sandbox)
  • 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/webhook set?
  • Webhook secret di Pakasir dashboard match dengan PAKASIR_WEBHOOK_SECRET env 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/signals fanned-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.

Section 11

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: receivedparsedfanned-out (atau review / 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-Token header. 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.