0% found this document useful (0 votes)
6 views19 pages

Frontend API Guide

The OptiTrade Notification API guide outlines the integration process for frontend developers, detailing authentication requirements, endpoints for user notification preferences, price alerts management, and notification history. It includes examples of requests and responses, as well as frontend implementation snippets for various functionalities such as loading preferences, creating alerts, and managing notifications. Additionally, it covers real-time notifications via WebSocket and provides utilities for testing and retrieving notification statistics.

Uploaded by

Sameer K
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
6 views19 pages

Frontend API Guide

The OptiTrade Notification API guide outlines the integration process for frontend developers, detailing authentication requirements, endpoints for user notification preferences, price alerts management, and notification history. It includes examples of requests and responses, as well as frontend implementation snippets for various functionalities such as loading preferences, creating alerts, and managing notifications. Additionally, it covers real-time notifications via WebSocket and provides utilities for testing and retrieving notification statistics.

Uploaded by

Sameer K
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 19

OptiTrade Notification API - Frontend Developer

Guide
Overview
This guide provides everything a frontend developer needs to integrate Opti-
Trade’s notification system. All endpoints require JWT authentication and
return JSON responses.

Authentication
Required Header:
headers: {
'Authorization': 'Bearer YOUR_JWT_TOKEN',
'Content-Type': 'application/json'
}

Base URL
https://your-domain.com/notifications

User Notification Preferences


Get User Preferences
Endpoint: GET /preferences/{user_id}
What it does: Retrieves user’s notification settings. Creates default settings
if none exist.
Frontend Use Case: Load user preferences for settings page
Request:
fetch('/notifications/preferences/123', {
headers: { 'Authorization': 'Bearer token' }
})
Response:
{
"user_id": 123,
"price_alert_threshold": 5.0,
"portfolio_alert_threshold": 1000.0,
"email_enabled": true,
"push_enabled": true,
"quiet_hours_start": 22,
"quiet_hours_end": 6
}

1
Response Fields Explained: - price_alert_threshold: Percentage change
(5.0 = 5%) that triggers watchlist alerts - portfolio_alert_threshold: Dollar
amount ($1000) that triggers portfolio value alerts - email_enabled: Whether
user receives email notifications - push_enabled: Whether user receives push
notifications (future feature) - quiet_hours_start: Hour (0-23) when notifica-
tions stop (22 = 10 PM) - quiet_hours_end: Hour (0-23) when notifications
resume (6 = 6 AM)
Frontend Implementation:
// Settings form component
const [preferences, setPreferences] = useState({
price_alert_threshold: 5.0,
portfolio_alert_threshold: 1000.0,
email_enabled: true,
push_enabled: true,
quiet_hours_start: 22,
quiet_hours_end: 6
});

// Load preferences on component mount


useEffect(() => {
loadUserPreferences();
}, []);

const loadUserPreferences = async () => {


const response = await fetch(`/notifications/preferences/${userId}`, {
headers: { 'Authorization': `Bearer ${token}` }
});
const data = await response.json();
setPreferences(data);
};

Update User Preferences


Endpoint: PUT /preferences/{user_id}
What it does: Updates user’s notification settings. Only send fields you want
to change.
Frontend Use Case: Save settings form changes
Request Body (all fields optional):
{
"price_alert_threshold": 2.5,
"portfolio_alert_threshold": 500.0,
"email_enabled": false,
"push_enabled": true,

2
"quiet_hours_start": 23,
"quiet_hours_end": 7
}
Field Validation: - price_alert_threshold: 0.1 to 100.0 (percentage) -
portfolio_alert_threshold: 1.0 to 1000000.0 (dollars) - quiet_hours_start/end:
0 to 23 (24-hour format)
Response: Same as GET preferences
Frontend Implementation:
const updatePreferences = async (changes) => {
try {
const response = await fetch(`/notifications/preferences/${userId}`, {
method: 'PUT',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(changes)
});

if (response.ok) {
const updated = await response.json();
setPreferences(updated);
showSuccessMessage('Preferences updated successfully');
}
} catch (error) {
showErrorMessage('Failed to update preferences');
}
};

// Usage in form
const handleSubmit = (formData) => {
updatePreferences({
price_alert_threshold: parseFloat(formData.priceThreshold),
email_enabled: formData.emailEnabled
});
};

Price Alerts Management


Create Price Alert
Endpoint: POST /price-alerts/{user_id}
What it does: Creates a new price alert for a stock symbol

3
Frontend Use Case: “Set Price Alert” button on stock pages
Request Body:
{
"symbol": "AAPL",
"target_price": 150.00,
"condition": "above"
}
Field Requirements: - symbol: Stock symbol (uppercase, 1-7 characters) -
target_price: Price threshold (positive number, 2 decimal places) - condition:
Either “above” or “below”
Response:
{
"id": 456,
"user_id": 123,
"symbol": "AAPL",
"target_price": 150.00,
"condition": "above",
"is_active": true,
"created_at": "2024-01-15T10:30:00Z",
"triggered_at": null
}
Response Fields: - id: Unique alert ID (use for deletion) - is_active: true =
monitoring, false = triggered or disabled - triggered_at: null if not triggered,
timestamp if triggered
Error Responses: - 400: Invalid condition or negative price - 409: Alert
already exists for this combination
Frontend Implementation:
const createPriceAlert = async (symbol, targetPrice, condition) => {
try {
const response = await fetch(`/notifications/price-alerts/${userId}`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
symbol: symbol.toUpperCase(),
target_price: parseFloat(targetPrice),
condition: condition
})
});

4
if (response.status === 409) {
showErrorMessage('Price alert already exists for this stock and price');
return;
}

if (response.ok) {
const alert = await response.json();
showSuccessMessage(`Price alert created for ${symbol} at $${targetPrice}`);
refreshAlertsList();
}
} catch (error) {
showErrorMessage('Failed to create price alert');
}
};

// Form validation
const validatePriceAlert = (symbol, price, condition) => {
if (!symbol || symbol.length > 7) return 'Invalid symbol';
if (price <= 0) return 'Price must be positive';
if (!['above', 'below'].includes(condition)) return 'Invalid condition';
return null;
};

Get User’s Price Alerts


Endpoint: GET /price-alerts/{user_id}?active_only=true
What it does: Returns list of user’s price alerts
Frontend Use Case: Display alerts in user dashboard
Query Parameters: - active_only: true (default) = only active alerts, false
= all alerts
Response:
[
{
"id": 456,
"user_id": 123,
"symbol": "AAPL",
"target_price": 150.00,
"condition": "above",
"is_active": true,
"created_at": "2024-01-15T10:30:00Z",
"triggered_at": null
},

5
{
"id": 457,
"user_id": 123,
"symbol": "TSLA",
"target_price": 200.00,
"condition": "below",
"is_active": false,
"created_at": "2024-01-14T15:20:00Z",
"triggered_at": "2024-01-15T09:45:00Z"
}
]
Frontend Implementation:
const [alerts, setAlerts] = useState([]);
const [showTriggered, setShowTriggered] = useState(false);

const loadAlerts = async () => {


const response = await fetch(
`/notifications/price-alerts/${userId}?active_only=${!showTriggered}`,
{ headers: { 'Authorization': `Bearer ${token}` } }
);
const data = await response.json();
setAlerts(data);
};

// Alert list component


const AlertsList = () => (
<div>
<button onClick={() => setShowTriggered(!showTriggered)}>
{showTriggered ? 'Show Active Only' : 'Show All Alerts'}
</button>

{alerts.map(alert => (
<div key={alert.id} className={alert.is_active ? 'active' : 'triggered'}>
<span>{alert.symbol}</span>
<span>${alert.target_price}</span>
<span>{alert.condition}</span>
<span>{alert.is_active ? 'Active' : 'Triggered'}</span>
{alert.is_active && (
<button onClick={() => deleteAlert(alert.id)}>Delete</button>
)}
</div>
))}
</div>
);

6
Delete Price Alert
Endpoint: DELETE /price-alerts/{alert_id}
What it does: Removes a price alert
Frontend Use Case: Delete button in alerts list
Response:
{
"message": "Price alert deleted successfully"
}
Frontend Implementation:
const deleteAlert = async (alertId) => {
if (!confirm('Delete this price alert?')) return;

try {
const response = await fetch(`/notifications/price-alerts/${alertId}`, {
method: 'DELETE',
headers: { 'Authorization': `Bearer ${token}` }
});

if (response.ok) {
setAlerts(alerts.filter(alert => alert.id !== alertId));
showSuccessMessage('Price alert deleted');
}
} catch (error) {
showErrorMessage('Failed to delete alert');
}
};

Notification History
Get Notification History
Endpoint: GET /history/{user_id}?limit=50&offset=0&event_type=price_alert
What it does: Returns user’s notification history with pagination
Frontend Use Case: Notification center, history page
Query Parameters: - limit: Number of notifications (1-100, default: 50) -
offset: Skip notifications for pagination (default: 0) - event_type: Filter by
type (optional)
Event Types: - price_alert: Price threshold notifications - transaction_complete:
Buy/sell confirmations - portfolio_change: Portfolio value changes - test:
Test notifications

7
Response:
[
{
"id": 789,
"event_type": "price_alert",
"title": "Price Alert: AAPL",
"message": "AAPL has reached $150.25 (target: $150.00)",
"created_at": "2024-01-15T10:30:00Z",
"status": "delivered"
},
{
"id": 790,
"event_type": "transaction_complete",
"title": "Transaction Complete",
"message": "Successfully bought 100 shares of TSLA at $195.50",
"created_at": "2024-01-15T09:15:00Z",
"status": "delivered"
}
]
Status Values: - delivered: Successfully sent - pending: Waiting to be sent
- failed: Delivery failed
Frontend Implementation:
const [notifications, setNotifications] = useState([]);
const [loading, setLoading] = useState(false);
const [hasMore, setHasMore] = useState(true);

const loadNotifications = async (offset = 0, eventType = null) => {


setLoading(true);

let url = `/notifications/history/${userId}?limit=20&offset=${offset}`;


if (eventType) url += `&event_type=${eventType}`;

const response = await fetch(url, {


headers: { 'Authorization': `Bearer ${token}` }
});

const data = await response.json();

if (offset === 0) {
setNotifications(data);
} else {
setNotifications(prev => [...prev, ...data]);
}

8
setHasMore(data.length === 20);
setLoading(false);
};

// Infinite scroll implementation


const loadMore = () => {
if (!loading && hasMore) {
loadNotifications(notifications.length);
}
};

// Filter by event type


const filterByType = (eventType) => {
setNotifications([]);
loadNotifications(0, eventType);
};

Testing & Utilities


Send Test Notification
Endpoint: POST /test/{user_id}
What it does: Sends a test notification to verify user’s settings
Frontend Use Case: “Test Notifications” button in settings
Response:
{
"message": "Test notification queued successfully"
}
Frontend Implementation:
const sendTestNotification = async () => {
try {
const response = await fetch(`/notifications/test/${userId}`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${token}` }
});

if (response.ok) {
showSuccessMessage('Test notification sent! Check your email.');
}
} catch (error) {
showErrorMessage('Failed to send test notification');
}
};

9
Get Notification Statistics
Endpoint: GET /stats/{user_id}
What it does: Returns user’s notification statistics
Frontend Use Case: Dashboard metrics, settings page info
Response:
{
"total_notifications": 150,
"delivered_notifications": 147,
"pending_notifications": 2,
"active_price_alerts": 5,
"delivery_rate": 98.0
}
Response Fields: - total_notifications: All notifications ever sent
to user - delivered_notifications: Successfully delivered count -
pending_notifications: Currently waiting to be sent - active_price_alerts:
Number of active price alerts - delivery_rate: Percentage of successful deliv-
eries
Frontend Implementation:
const [stats, setStats] = useState(null);

const loadStats = async () => {


const response = await fetch(`/notifications/stats/${userId}`, {
headers: { 'Authorization': `Bearer ${token}` }
});
const data = await response.json();
setStats(data);
};

// Stats display component


const NotificationStats = () => (
<div className="stats-grid">
<div className="stat-card">
<h3>Total Notifications</h3>
<span>{stats?.total_notifications || 0}</span>
</div>
<div className="stat-card">
<h3>Delivery Rate</h3>
<span>{stats?.delivery_rate || 0}%</span>
</div>
<div className="stat-card">
<h3>Active Alerts</h3>

10
<span>{stats?.active_price_alerts || 0}</span>
</div>
<div className="stat-card">
<h3>Pending</h3>
<span>{stats?.pending_notifications || 0}</span>
</div>
</div>
);

WebSocket Real-Time Notifications


Connection Setup
Endpoint: WS /ws/{user_id}
What it does: Establishes real-time connection for instant notifications
Frontend Use Case: Live notifications, real-time price updates
Connection Example:
class NotificationWebSocket {
constructor(userId, token) {
this.userId = userId;
this.token = token;
this.ws = null;
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
}

connect() {
// Note: WebSocket doesn't support custom headers, so token in URL
this.ws = new WebSocket(`ws://your-domain.com/ws/${this.userId}`);

this.ws.onopen = () => {
console.log('WebSocket connected');
this.reconnectAttempts = 0;
this.sendPing();
};

this.ws.onmessage = (event) => {


const data = JSON.parse(event.data);
this.handleMessage(data);
};

this.ws.onclose = () => {
console.log('WebSocket disconnected');
this.reconnect();

11
};

this.ws.onerror = (error) => {


console.error('WebSocket error:', error);
};
}

handleMessage(data) {
switch (data.type) {
case 'notification':
this.showNotification(data);
break;
case 'price_update':
this.updatePrices(data.data);
break;
case 'pong':
// Heartbeat response
break;
}
}

showNotification(notification) {
// Display notification in UI
const toast = {
id: notification.id,
title: notification.title,
message: notification.message,
type: notification.event_type,
timestamp: notification.created_at
};

// Add to notification center


addToNotificationCenter(toast);

// Show toast notification


showToast(toast);

// Play notification sound


if (notification.priority <= 2) {
playNotificationSound();
}
}

updatePrices(priceData) {
// Update price displays in UI
priceData.forEach(item => {

12
updateStockPrice(item.symbol, item.price);
});
}

sendPing() {
if (this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({ type: 'ping' }));
setTimeout(() => this.sendPing(), 30000); // Ping every 30 seconds
}
}

subscribeToPrices() {
if (this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({ type: 'subscribe_prices' }));
}
}

reconnect() {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
const delay = Math.pow(2, this.reconnectAttempts) * 1000; // Exponential backoff
setTimeout(() => this.connect(), delay);
}
}

disconnect() {
if (this.ws) {
this.ws.close();
}
}
}

// Usage in React component


const useNotificationWebSocket = (userId, token) => {
const [ws, setWs] = useState(null);
const [connected, setConnected] = useState(false);

useEffect(() => {
const websocket = new NotificationWebSocket(userId, token);
websocket.connect();
setWs(websocket);

return () => {
websocket.disconnect();
};
}, [userId, token]);

13
return { ws, connected };
};

WebSocket Message Types


Incoming Messages Notification Message:
{
"type": "notification",
"id": 456,
"event_type": "price_alert",
"title": "Price Alert: AAPL",
"message": "AAPL has reached $150.25",
"priority": 2,
"data": {
"symbol": "AAPL",
"current_price": 150.25,
"target_price": 150.00,
"condition": "above"
},
"created_at": "2024-01-15T10:30:00Z"
}
Price Update Message:
{
"type": "price_update",
"data": [
{
"symbol": "AAPL",
"price": 150.25,
"time_fetched": "2024-01-15 10:30:00"
}
],
"timestamp": "2024-01-15T10:30:00Z"
}

Outgoing Messages Ping (Heartbeat):


{
"type": "ping"
}
Subscribe to Price Updates:
{
"type": "subscribe_prices"
}

14
Error Handling
Standard Error Response
{
"detail": "Error description",
"error_code": "SPECIFIC_ERROR_CODE",
"timestamp": "2024-01-15T10:30:00Z"
}

Common HTTP Status Codes


• 200 OK: Request successful
• 201 Created: Resource created
• 400 Bad Request: Invalid request data
• 401 Unauthorized: Missing or invalid token
• 404 Not Found: Resource not found
• 409 Conflict: Resource already exists
• 422 Unprocessable Entity: Validation error
• 429 Too Many Requests: Rate limit exceeded
• 500 Internal Server Error: Server error

Frontend Error Handling


const handleApiError = async (response) => {
if (!response.ok) {
const error = await response.json();

switch (response.status) {
case 401:
// Redirect to login
redirectToLogin();
break;
case 409:
showErrorMessage('This alert already exists');
break;
case 429:
showErrorMessage('Too many requests. Please wait a moment.');
break;
default:
showErrorMessage(error.detail || 'An error occurred');
}

throw new Error(error.detail);


}

return response.json();

15
};

// Usage
const createAlert = async (data) => {
try {
const response = await fetch('/notifications/price-alerts/123', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});

const result = await handleApiError(response);


return result;
} catch (error) {
console.error('Failed to create alert:', error);
}
};

Rate Limits
Per User Limits: - Notification creation: 100 requests/hour - Price alerts:
50 active alerts maximum - WebSocket connections: 5 concurrent connections -
Test notifications: 10 per hour
Frontend Implementation:
// Rate limit tracking
const rateLimiter = {
testNotifications: { count: 0, resetTime: Date.now() + 3600000 },

canSendTest() {
const now = Date.now();
if (now > this.testNotifications.resetTime) {
this.testNotifications = { count: 0, resetTime: now + 3600000 };
}
return this.testNotifications.count < 10;
},

recordTestSent() {
this.testNotifications.count++;
}
};

const sendTestNotification = async () => {

16
if (!rateLimiter.canSendTest()) {
showErrorMessage('Test notification limit reached. Try again in an hour.');
return;
}

// Send test notification


rateLimiter.recordTestSent();
};

Complete Integration Example


// Complete notification system integration
class NotificationManager {
constructor(userId, token) {
this.userId = userId;
this.token = token;
this.ws = null;
this.preferences = null;
}

async initialize() {
// Load user preferences
await this.loadPreferences();

// Connect WebSocket
this.connectWebSocket();

// Load initial data


await this.loadAlerts();
await this.loadNotificationHistory();
}

async loadPreferences() {
const response = await fetch(`/notifications/preferences/${this.userId}`, {
headers: { 'Authorization': `Bearer ${this.token}` }
});
this.preferences = await response.json();
}

connectWebSocket() {
this.ws = new NotificationWebSocket(this.userId, this.token);
this.ws.connect();
}

async createPriceAlert(symbol, targetPrice, condition) {


const response = await fetch(`/notifications/price-alerts/${this.userId}`, {

17
method: 'POST',
headers: {
'Authorization': `Bearer ${this.token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ symbol, target_price: targetPrice, condition })
});

return handleApiError(response);
}

async updatePreferences(changes) {
const response = await fetch(`/notifications/preferences/${this.userId}`, {
method: 'PUT',
headers: {
'Authorization': `Bearer ${this.token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(changes)
});

this.preferences = await handleApiError(response);


return this.preferences;
}
}

// Usage in React app


const App = () => {
const [notificationManager, setNotificationManager] = useState(null);

useEffect(() => {
const manager = new NotificationManager(userId, token);
manager.initialize();
setNotificationManager(manager);

return () => {
manager.ws?.disconnect();
};
}, [userId, token]);

return (
<div>
{/* Your app components */ }
</div>
);
};

18
“‘

19

You might also like