Run these commands in order. Each step builds on the previous one. Make sure the HTTP server (
npm run start:dev) and worker (npm run start:worker) are running.
Verify the server is running.
Invoke-RestMethod -Uri http://localhost:3001/api/healthExpected: { status: "ok", service: "market-sentiment-api", ... }
Create a new user account.
Invoke-RestMethod -Method Post -Uri http://localhost:3001/api/auth/signup `
-ContentType "application/json" `
-Body '{"email": "test@example.com", "password": "Test1234!", "name": "Test User"}'Expected: Returns accessToken + user object.
Login and store the token for all future requests.
$login = Invoke-RestMethod -Method Post -Uri http://localhost:3001/api/auth/login `
-ContentType "application/json" `
-Body '{"email": "test@example.com", "password": "Test1234!"}'
$token = $login.accessToken
$tokenExpected: Prints a long JWT string. All commands below use $token.
Verify the logged-in user.
Invoke-RestMethod -Uri http://localhost:3001/api/users/me `
-Headers @{ Authorization = "Bearer $token" }Expected: Returns user object with id, email, name.
See what assets are available to track.
$assets = Invoke-RestMethod -Uri http://localhost:3001/api/assets
$assets | Select-Object id, symbol, name, type | Format-TableExpected: Table with BTC, ETH, SOL, TSLA, AAPL.
Add assets to your watchlist. Alerts only fire for tracked assets.
# Track all 5 assets
foreach ($asset in $assets) {
Invoke-RestMethod -Method Post -Uri http://localhost:3001/api/assets/add `
-Headers @{ Authorization = "Bearer $token" } `
-ContentType "application/json" `
-Body "{`"assetId`": `"$($asset.id)`"}"
Write-Host "Tracked: $($asset.symbol)"
}Expected: "Tracked: BTC", "Tracked: ETH", etc.
Invoke-RestMethod -Uri http://localhost:3001/api/assets/tracked/me `
-Headers @{ Authorization = "Bearer $token" } | ConvertTo-Json -Depth 2Expected: Array of 5 tracked assets.
Check alert toggle status for each tracked asset.
Invoke-RestMethod -Uri http://localhost:3001/api/preferences `
-Headers @{ Authorization = "Bearer $token" } | ConvertTo-Json -Depth 3Expected: Array of preferences with alertEnabled: true for each asset.
# Toggle alert off for first asset
$firstAssetId = $assets[0].id
Invoke-RestMethod -Method Patch `
-Uri "http://localhost:3001/api/preferences/$firstAssetId/toggle-alert" `
-Headers @{ Authorization = "Bearer $token" } | ConvertTo-JsonExpected: Returns preference with alertEnabled: false. Run again to toggle back to true.
See which social channels are available to follow.
$channels = Invoke-RestMethod -Uri http://localhost:3001/api/channels `
-Headers @{ Authorization = "Bearer $token" }
$channels.defaults | Select-Object id, platform, handle, displayName | Format-TableExpected: Table with elonmusk, POTUS, VitalikButerin, etc.
Follow default channels so the fetcher pulls real tweets.
# Follow all default channels
foreach ($ch in $channels.defaults) {
Invoke-RestMethod -Method Post -Uri http://localhost:3001/api/channels/follow `
-Headers @{ Authorization = "Bearer $token" } `
-ContentType "application/json" `
-Body "{`"channelId`": `"$($ch.id)`"}"
Write-Host "Followed: @$($ch.handle)"
}Expected: "Followed: @elonmusk", "Followed: @POTUS", etc.
Add a Twitter account that isn't in the defaults.
Invoke-RestMethod -Method Post -Uri http://localhost:3001/api/channels/custom `
-Headers @{ Authorization = "Bearer $token" } `
-ContentType "application/json" `
-Body '{"platform": "TWITTER", "handle": "@CoinBureau", "displayName": "Coin Bureau"}' | ConvertTo-JsonExpected: Returns the created channel object. Auto-followed.
Invoke-RestMethod -Uri http://localhost:3001/api/channels/followed `
-Headers @{ Authorization = "Bearer $token" } | ConvertTo-Json -Depth 2Expected: Array of all channels you're following.
# Unfollow the first channel
$followed = Invoke-RestMethod -Uri http://localhost:3001/api/channels/followed `
-Headers @{ Authorization = "Bearer $token" }
Invoke-RestMethod -Method Delete -Uri http://localhost:3001/api/channels/unfollow `
-Headers @{ Authorization = "Bearer $token" } `
-ContentType "application/json" `
-Body "{`"channelId`": `"$($followed[0].id)`"}"Expected: { "message": "Unfollowed successfully" }
Lower thresholds so alerts trigger on almost every post.
Invoke-RestMethod -Method Post -Uri http://localhost:3001/api/strategies/strategy/update `
-Headers @{ Authorization = "Bearer $token" } `
-ContentType "application/json" `
-Body '{"impactThreshold": 5, "confidenceThreshold": 0.1, "sentimentThreshold": 0.05}' | ConvertTo-Json -Depth 2Expected: Returns strategy object with updated thresholds.
Invoke-RestMethod -Uri http://localhost:3001/api/strategies/strategy `
-Headers @{ Authorization = "Bearer $token" } | ConvertTo-Json -Depth 2Expected: Shows your active strategy config with the low thresholds.
Insert a test post to trigger the sentiment pipeline.
$body = @{
assetId = $assets[0].id
source = "TWITTER"
content = "Bitcoin is going to the moon! Massive whale just bought 10000 BTC. Extremely bullish!"
author = "test_user"
} | ConvertTo-Json
Invoke-RestMethod -Method Post -Uri http://localhost:3001/api/posts `
-Headers @{ Authorization = "Bearer $token" } `
-ContentType "application/json" `
-Body $body | ConvertTo-Json -Depth 2Expected: Returns created post. Check worker logs for sentiment processing.
# All posts (latest 20)
Invoke-RestMethod -Uri "http://localhost:3001/api/posts?limit=20" `
-Headers @{ Authorization = "Bearer $token" } | ConvertTo-Json -Depth 3
# Filter by asset
Invoke-RestMethod -Uri "http://localhost:3001/api/posts?assetId=$($assets[0].id)&limit=10" `
-Headers @{ Authorization = "Bearer $token" } | ConvertTo-Json -Depth 3
# Filter by source
Invoke-RestMethod -Uri "http://localhost:3001/api/posts?source=TWITTER&limit=10" `
-Headers @{ Authorization = "Bearer $token" } | ConvertTo-Json -Depth 3Expected: Array of posts with nested sentiment and asset objects.
$posts = Invoke-RestMethod -Uri "http://localhost:3001/api/posts?limit=1" `
-Headers @{ Authorization = "Bearer $token" }
$postId = $posts[0].id
Invoke-RestMethod -Uri "http://localhost:3001/api/posts/$postId" `
-Headers @{ Authorization = "Bearer $token" } | ConvertTo-Json -Depth 3Expected: Single post with full sentiment details.
Run on-demand AI analysis on a post.
$posts = Invoke-RestMethod -Uri "http://localhost:3001/api/posts?limit=1" `
-Headers @{ Authorization = "Bearer $token" }
Invoke-RestMethod -Method Post -Uri http://localhost:3001/api/analysis/deep `
-Headers @{ Authorization = "Bearer $token" } `
-ContentType "application/json" `
-Body "{`"postId`": `"$($posts[0].id)`"}" | ConvertTo-Json -Depth 2Expected: Returns summary, sentiment, reasoning, keyThemes, riskLevel, recommendation. Returns mock analysis if OpenAI key not configured.
Manually check if a post looks like whale activity.
Invoke-RestMethod -Method Post -Uri http://localhost:3001/api/whale/check `
-Headers @{ Authorization = "Bearer $token" } `
-ContentType "application/json" `
-Body '{"content": "Whale alert! 5000 BTC just moved from unknown wallet", "authorFollowers": 500000, "retweetCount": 5000, "likeCount": 20000}' | ConvertTo-JsonExpected: { isWhale: true, reason: "...", confidenceBoost: 0.15 }
Wait ~30 seconds for the fetcher to poll and alerts to fire, then check.
$alerts = Invoke-RestMethod -Uri http://localhost:3001/api/alerts `
-Headers @{ Authorization = "Bearer $token" }
$alerts | Select-Object id, type, message, createdAt | Format-Table -Wrap
# Full details
$alerts | ConvertTo-Json -Depth 3Expected: Array of IN_APP alerts with sentiment details in metadata.
Open your browser at http://localhost:3000 (or any page), open DevTools Console, and paste:
// Load Socket.IO client (CDN)
var script = document.createElement('script');
script.src = 'https://cdn.socket.io/4.7.5/socket.io.min.js';
script.onload = function() {
var socket = io('http://localhost:3001');
socket.on('connect', function() {
console.log('Connected! Socket ID:', socket.id);
});
socket.on('new-post', function(data) {
console.log('NEW POST:', data);
});
socket.on('new-sentiment', function(data) {
console.log('NEW SENTIMENT:', data);
});
socket.on('new-alert-broadcast', function(data) {
console.log('NEW ALERT:', data);
});
socket.on('disconnect', function() {
console.log('Disconnected');
});
};
document.head.appendChild(script);Expected: Every 30 seconds when the fetcher polls, you'll see NEW POST and NEW SENTIMENT messages. If thresholds are low, you'll also see NEW ALERT.
After testing, set realistic thresholds.
Invoke-RestMethod -Method Post -Uri http://localhost:3001/api/strategies/strategy/update `
-Headers @{ Authorization = "Bearer $token" } `
-ContentType "application/json" `
-Body '{"impactThreshold": 70, "confidenceThreshold": 0.6, "sentimentThreshold": 0.2, "keywordsPositive": ["bullish", "moon", "breakout"], "keywordsNegative": ["crash", "dump", "bearish"]}' | ConvertTo-Json -Depth 2Expected: Only high-impact, high-confidence posts will trigger alerts now.
After running the above commands, your worker terminal should show a chain like:
[FetcherService] 📥 Polled 6 channels — enqueued 12 posts
[PostWorker] ⚙️ job=1 asset=BTC
[PostWorker] 💾 Post stored [uuid]
[PostWorker] 📤 Post [uuid] → sentimentQueue
[SentimentWorker] 🧠 job=1 post=uuid
[SentimentWorker] ✅ Sentiment stored [uuid] — score=0.55 impact=67 category=SOCIAL_BUZZ whale=false
[SentimentWorker] 🔍 Evaluate user=[uuid] shouldAlert=true
[AlertsService] Alert [IN_APP] created for user uuid
[SentimentWorker] 🔔 Alert triggered for user [uuid]
[EmailService] 📧 Email sent to user@gmail.com ← only if SMTP configured
| Problem | Solution |
|---|---|
401 Unauthorized |
Re-run Step 3 (login) to refresh $token |
404 Not Found |
Check the URL has /api/ prefix |
| No alerts firing | Lower thresholds (Step 15) and track assets (Step 6) |
| No posts appearing | Make sure worker is running (npm run start:worker) |
| Email not sending | Set SMTP_PASS to Gmail App Password (not regular password) |
| Twitter fetch failing | Check TWITTER_BEARER_TOKEN in .env, may be rate-limited |
| WebSocket not connecting | Make sure HTTP server is running, check CORS origin |