Server-Sent Events (SSE)

Info

Draft Documentation - This section is currently under review and may be subject to changes.

Server-Sent Events (SSE) Endpoints

This section displays the Server-Sent Events (SSE) implementation for real-time player notifications. SSE provides a one-way communication channel from the server to the client, enabling instant delivery of notifications and updates without the need for polling.

Service: go_gateway Protocol: HTTP with text/event-stream content type

Overview

The SSE system allows players to maintain a persistent connection to receive real-time events as they occur. Events are published via Redis Pub/Sub and delivered to all connected clients subscribed to the player’s event stream.

Features

  • Real-time Delivery: Instant notification delivery as events occur
  • Connection Management: Automatic keep-alive heartbeats every 15 seconds
  • Multi-tenant Support: Separate event channels per brand and player
  • Scalability: Connection limit configuration (default: 50,000 concurrent connections)
  • Resilience: Automatic reconnection handling and buffered event delivery
  • Event Types: Extensible event system supporting various notification types

Architecture

Client → SSE Connection → Gateway → Redis Pub/Sub → Event Publishers

Event Channel Pattern: SSE::{brand_id}::{player_id}

Available Operations

  • GET - Establish SSE connection and receive real-time events

Event Types

The SSE system currently supports the following event types:

  • notification - In-app notifications (bonuses, promotions, account updates)

Additional event types can be added as the platform evolves.

Connection Lifecycle

  1. Client initiates SSE connection with authentication token
  2. Gateway validates brand and player authentication
  3. Initial “connected” event sent to client
  4. Event subscription established to Redis channel
  5. Keep-alive heartbeats sent every 15 seconds
  6. Events published to Redis are immediately forwarded to client
  7. Connection remains open until client disconnects or error occurs

Configuration

Parameter Default Description
SSE_CONNECTION_LIMIT 50000 Maximum concurrent SSE connections
EVENTS_REDIS_STORAGE - Redis host for SSE event publishing
keepAlivePeriod 15s Heartbeat interval for connection health
eventChannelBufferSize 50 Event buffer size for burst handling

Subsections of Server-Sent Events (SSE)

SSE Stream Connection

Info

Draft Documentation - This section is currently under review and may be subject to changes.

This endpoint establishes a Server-Sent Events (SSE) connection for receiving real-time notifications and updates. The connection remains open indefinitely, with automatic keep-alive heartbeats every 15 seconds.

Request (GET)

/gateway/sse/{brand_id}/stream

Parameters:

Name In Type Required Description
brand_id path int true The ID of the casino brand.

Headers:

Name Type Required Description
x-auth-token string true Player authentication token.
Accept string true Must be set to text/event-stream.

Response

Status 200 (Connection Established)

Content-Type: text/event-stream; charset=utf-8

Initial Connection Message

Upon successful connection, the server sends an initial “connected” event:

data: {"type":"connected","timestamp":"2025-10-22T14:30:45Z"}

Connected Message Format:

Field Type Description
type string Event type (“connected”).
timestamp string ISO 8601 timestamp in RFC3339 format.

Keep-alive Messages

The server sends keep-alive heartbeats every 15 seconds to maintain the connection:

: keepalive 2025-10-22 14:30:45
data: {"type":"keepalive","timestamp":"2025-10-22 14:30:45"}

Keep-alive Message Format:

Field Type Description
type string Event type (“keepalive”).
timestamp string Timestamp in format “YYYY-MM-DD HH:MM:SS”.

Note: The first line (starting with :) is an SSE comment containing the timestamp. The second line contains the JSON data payload.

Event Messages

When events occur, they are immediately pushed to the client:

data: {"brand_id":1001,"player_id":12345,"event_type":"notification","event_data":{"message_id":"a7f8c934-1b2d-4e5f-9c8a-7d6e5f4a3b2c","event":"bonus_granted","event_details":{"bonus_amount":100,"bonus_type":"welcome"}}}

Event Message Format:

Field Type Description
brand_id int Brand identifier.
player_id int Player identifier.
event_type string Type of event (e.g., “notification”).
event_data object Event-specific data payload (structure varies by type).

Example Connection Flow

1. Client Initiates Connection

const eventSource = new EventSource(
  '/gateway/sse/1001/stream',
  {
    headers: {
      'x-auth-token': 'e74af034-a346-4d5c-be97-f7ed83f373a7'
    }
  }
);

eventSource.onmessage = (event) => {
  const data = JSON.parse(event.data);
  console.log('Received event:', data);
};

eventSource.onerror = (error) => {
  console.error('SSE connection error:', error);
};

2. Server Sends Connected Event

data: {"type":"connected","timestamp":"2025-10-22T14:30:45Z"}

3. Server Sends Keep-alive (every 15 seconds)

: keepalive 2025-10-22 14:31:00
data: {"type":"keepalive","timestamp":"2025-10-22 14:31:00"}

4. Server Sends Real Event (when published)

data: {"brand_id":1001,"player_id":12345,"event_type":"notification","event_data":{"message_id":"a7f8c934-1b2d-4e5f-9c8a-7d6e5f4a3b2c","event":"bonus_granted","event_details":{"bonus_amount":100,"bonus_type":"welcome","currency":"USD"}}}

Error Responses

Status 400

{
  "status": "error",
  "message": "Bad Request - Invalid brand_id"
}

Status 401

{
  "status": "error",
  "message": "Unauthorized - Invalid or missing authentication token"
}

Status 429

{
  "status": "error",
  "message": "Too Many Requests - Connection limit reached"
}

Note: When the connection limit (SSE_CONNECTION_LIMIT) is reached, new connection attempts will be rejected with a 429 status code.

SSE Error Event (during active connection)

If an error occurs during an active connection, the server may send an error event:

data: {"type":"error","message":"Failed to subscribe to events"}

Connection Management

Connection Limits

  • Default Limit: 50,000 concurrent connections
  • Configuration: Set via SSE_CONNECTION_LIMIT environment variable
  • Behavior: When limit is reached, new connections receive HTTP 429

Connection Termination

Connections can be terminated by:

  1. Client Disconnect: Client closes the connection
  2. Server Error: Internal server error or Redis subscription failure
  3. Network Issue: Network interruption or timeout
  4. Authentication Expiry: Player token expires (requires reconnection)

Reconnection Strategy

Clients should implement automatic reconnection with exponential backoff:

let retryDelay = 1000; // Start with 1 second

eventSource.onerror = () => {
  eventSource.close();

  setTimeout(() => {
    // Reconnect
    eventSource = new EventSource('/gateway/sse/1001/stream', {
      headers: { 'x-auth-token': token }
    });

    // Increase delay for next retry (up to 30 seconds)
    retryDelay = Math.min(retryDelay * 2, 30000);
  }, retryDelay);
};

Implementation Notes

  • Redis Channel Pattern: Events are published to SSE::{brand_id}::{player_id}
  • Event Buffer: Server maintains a 50-event buffer to handle bursts
  • Keep-alive Interval: 15 seconds (prevents connection timeout)
  • HTTP Headers: Connection includes CORS headers and disables buffering
  • Player Context: Player ID is automatically extracted from the authentication token
  • Multi-instance Support: Works across multiple gateway instances via Redis Pub/Sub

Best Practices

  1. Single Connection: Maintain only one SSE connection per player session
  2. Handle Reconnection: Implement automatic reconnection with backoff
  3. Parse Events: Always parse the JSON data payload for event handling
  4. Monitor Keep-alive: Use keep-alive messages to detect connection health
  5. Close on Logout: Explicitly close the SSE connection when player logs out
  6. Error Handling: Implement proper error handling for network issues

See Also

SSE Events

Info

Draft Documentation - This section is currently under review and may be subject to changes.

SSE Event Types

This section documents the various event types that can be delivered via Server-Sent Events (SSE). Each event type has a specific structure and purpose.

Overview

SSE events are published to Redis using the channel pattern SSE::{brand_id}::{player_id} and delivered in real-time to connected clients. All events follow a consistent base structure with event-specific data payloads.

Event Structure

All SSE events (excluding system events like “connected” and “keepalive”) follow this structure:

{
  "brand_id": 1001,
  "player_id": 12345,
  "event_type": "notification",
  "event_data": {
    // Event-specific payload
  }
}

Base Fields

Field Type Description
brand_id int The casino brand identifier.
player_id int The player identifier.
event_type string The type of event (e.g., “notification”).
event_data object Event-specific data (structure varies by type).

System Events

These events are sent by the SSE system itself for connection management:

Connected Event

Sent immediately upon successful SSE connection establishment.

{
  "type": "connected",
  "timestamp": "2025-10-22T14:30:45Z"
}

Keep-alive Event

Sent every 15 seconds to maintain the connection.

: keepalive 2025-10-22 14:30:45
data: {"type":"keepalive","timestamp":"2025-10-22 14:30:45"}

Error Event

Sent when an error occurs during the SSE connection.

{
  "type": "error",
  "message": "Failed to subscribe to events"
}

Application Events

These events are published by various services in the platform:

Currently Available Events

Event Publishing

Events are published by backend services using the SSE service from go_casino_kit:

sseService.Publish(ctx, brandID, playerID, eventType, eventData)

This publishes to the Redis channel SSE::{brandID}::{playerID}, which the gateway listens to and forwards to connected clients.

Future Event Types

The SSE system is designed to be extensible. Future event types may include:

  • wallet_balance_update - Real-time wallet balance changes
  • game_result - Game round results and wins
  • tournament_update - Tournament status and leaderboard changes
  • message - Direct messages from support or system
  • promotion_trigger - Personalized promotion availability

Implementation Notes

  • All events are delivered in real-time (typically < 100ms latency)
  • Events are not persisted - if a client is disconnected, events during that time are lost
  • Each event is delivered as a separate SSE message with data: prefix
  • Events are JSON-formatted for easy parsing
  • Multiple events can be sent in rapid succession (buffer size: 50 events)

Subsections of SSE Events

Notification Event

Info

Draft Documentation - This section is currently under review and may be subject to changes.

The notification event is sent via SSE when a new in-app notification is created for a player. This provides real-time delivery of important updates such as bonuses, promotions, account changes, and other player-relevant information.

Event Type

event_type: notification

Event Structure

{
  "brand_id": 1001,
  "player_id": 12345,
  "event_type": "notification",
  "event_data": {
    "message_id": "a7f8c934-1b2d-4e5f-9c8a-7d6e5f4a3b2c",
    "event": "bonus_granted",
    "event_details": {
      "bonus_amount": 100,
      "bonus_type": "welcome",
      "currency": "USD"
    },
    "is_read": false,
    "time": "2025-10-22 14:30:45"
  }
}

Fields

Base Event Fields

Field Type Description
brand_id int The casino brand identifier.
player_id int The player identifier.
event_type string Always “notification” for this event type.
event_data object Notification-specific data (see below).

Event Data Fields

Field Type Description
message_id string Unique notification identifier (UUID).
event string Notification event type (e.g., “bonus_granted”, “promotion_available”).
event_details object Event-specific details (flexible JSON structure).
is_read boolean Whether the notification has been read (always false for new notifications).
time string Notification creation timestamp in format “YYYY-MM-DD HH:MM:SS”.

Event Examples

Example 1: Bonus Granted Notification

This notification is sent when a player receives a bonus.

{
  "brand_id": 1001,
  "player_id": 12345,
  "event_type": "notification",
  "event_data": {
    "message_id": "a7f8c934-1b2d-4e5f-9c8a-7d6e5f4a3b2c",
    "event": "bonus_granted",
    "event_details": {
      "bonus_amount": 100,
      "bonus_type": "welcome",
      "currency": "USD",
      "bonus_id": 789
    },
    "is_read": false,
    "time": "2025-10-22 14:30:45"
  }
}

Example 2: Promotion Available Notification

This notification is sent when a new promotion becomes available for a player.

{
  "brand_id": 1001,
  "player_id": 12345,
  "event_type": "notification",
  "event_data": {
    "message_id": "b8e9d045-2c3e-5f6g-0d9b-8e7f6g5b4c3d",
    "event": "promotion_available",
    "event_details": {
      "promo_id": "PROMO123",
      "promo_name": "Weekend Reload Bonus",
      "expires_at": "2025-11-15",
      "conditions": {
        "min_bet": 100,
        "multiplier": 3,
        "max_bonus": 500
      }
    },
    "is_read": false,
    "time": "2025-10-22 15:00:00"
  }
}

Example 3: Deposit Success Notification

This notification is sent after a successful deposit.

{
  "brand_id": 1001,
  "player_id": 12345,
  "event_type": "notification",
  "event_data": {
    "message_id": "c9f0e156-3d4f-6g7h-1e0c-9f8g7h6c5d4e",
    "event": "deposit_success",
    "event_details": {
      "amount": 250.00,
      "currency": "USD",
      "payment_method": "credit_card",
      "transaction_id": "TXN987654"
    },
    "is_read": false,
    "time": "2025-10-22 16:15:30"
  }
}

Example 4: KYC Approved Notification

This notification is sent when KYC verification is approved.

{
  "brand_id": 1001,
  "player_id": 12345,
  "event_type": "notification",
  "event_data": {
    "message_id": "d0g1f267-4e5g-7h8i-2f1d-0g9h8i7d6e5f",
    "event": "kyc_approved",
    "event_details": {
      "verification_level": "basic",
      "approved_at": "2025-10-22 17:00:00",
      "limits_updated": true
    },
    "is_read": false,
    "time": "2025-10-22 17:00:05"
  }
}

Example 5: Account Update Notification

This notification is sent for important account changes.

{
  "brand_id": 1001,
  "player_id": 12345,
  "event_type": "notification",
  "event_data": {
    "message_id": "e1h2g378-5f6h-8i9j-3g2e-1h0i9j8e7f6g",
    "event": "account_update",
    "event_details": {
      "update_type": "email_changed",
      "new_email": "new****@example.com",
      "requires_verification": true
    },
    "is_read": false,
    "time": "2025-10-22 18:00:00"
  }
}

Example 6: Tournament Win Notification

This notification is sent when a player wins a tournament.

{
  "brand_id": 1001,
  "player_id": 12345,
  "event_type": "notification",
  "event_data": {
    "message_id": "f2i3h489-6g7i-9j0k-4h3f-2i1j0k9f8g7h",
    "event": "tournament_win",
    "event_details": {
      "tournament_id": "123456-13456-123456-123456",
      "transaction_id": "123123",
      "tournament_name": "Tournament Name",
      "tournament_points": 1500,
      "tournament_start_date": "2025-08-20 00:00:00",
      "tournament_end_date": "2025-08-27 00:00:00",
      "tournament_position": 1,
      "template_id": "1",
      "prize_type": 1,
      "player_id": 12345,
      "casino_id": 1001,
      "prize_value": "100",
      "prize_name": "test",
      "total_rounds": 10,
      "total_bets": "450",
      "total_wins": "1425",
      "timestamp": "2025-08-27 00:00:00"
    },
    "is_read": false,
    "time": "2025-08-27 00:00:05"
  }
}

Common Event Types

The notification system supports various event types. Here are the most common:

Event Type Description
bonus_granted Player received a bonus
promotion_available New promotion available for player
deposit_success Successful deposit transaction
withdrawal_approved Withdrawal request approved
kyc_approved KYC verification approved
kyc_rejected KYC verification rejected
account_update Important account changes
tournament_update Tournament status or position change
tournament_win Player won a tournament
limit_reminder Responsible gaming limit reminder
document_required Additional documents needed

Event Details Structure

The event_details field is a flexible JSON object that can contain any event-specific data. The structure varies based on the event type. Common patterns include:

  • Amount-based events: Include amount, currency fields
  • Time-based events: Include expires_at, valid_until fields
  • Status updates: Include status, previous_status fields
  • Reference data: Include transaction_id, bonus_id, promo_id fields

Client Implementation

JavaScript Example

const eventSource = new EventSource('/gateway/sse/1001/stream', {
  headers: {
    'x-auth-token': playerToken
  }
});

eventSource.onmessage = (event) => {
  const data = JSON.parse(event.data);

  // Filter for notification events
  if (data.event_type === 'notification') {
    const notification = data.event_data;

    // Handle based on event type
    switch (notification.event) {
      case 'bonus_granted':
        showBonusNotification(notification);
        break;
      case 'promotion_available':
        showPromotionNotification(notification);
        break;
      case 'deposit_success':
        showDepositConfirmation(notification);
        break;
      default:
        showGenericNotification(notification);
    }

    // Update notification center
    addToNotificationCenter(notification);
  }
};

function showBonusNotification(notification) {
  const { bonus_amount, currency, bonus_type } = notification.event_details;
  showToast(`You received a ${bonus_type} bonus of ${bonus_amount} ${currency}!`);
  playNotificationSound();
  incrementNotificationBadge();
}

Publishing Notification Events

Notification events are automatically published by the go_marketing service when a new notification is created:

// From go_marketing/services/notifications_service.go
func (service *NotificationsService) InsertNewNotifications(
  ctx context.Context,
  notifications []domain.Notification
) error {
  // Insert into database
  err := service.notificationsDao.InsertNotification(ctx, notifications)
  if err != nil {
    return err
  }

  // Publish SSE event for each notification
  for _, notification := range notifications {
    eventData := map[string]interface{}{
      "message_id":    notification.MessageID,
      "event":         notification.Event,
      "event_details": notification.EventDetails,
      "is_read":       notification.IsRead,
      "time":          notification.Time.Format("2006-01-02 15:04:05"),
    }

    service.sseService.Publish(
      ctx,
      notification.BrandId,
      notification.PlayerId,
      sse.SSENotificationEvent,
      eventData,
    )
  }

  return nil
}

Integration with Notification Center

When a notification event is received via SSE:

  1. Display a real-time toast/popup notification to the player
  2. Add the notification to the notification center UI
  3. Increment the unread notification badge count
  4. Optionally play a sound or show a browser notification

To retrieve the full list of notifications (including older ones), use the Get Notifications endpoint.

To mark notifications as read, use the Mark Notifications as Read endpoint.

Performance Considerations

  • Delivery Latency: Typical delivery time is < 100ms from publication to client
  • Buffer Size: The SSE system can buffer up to 50 events per connection
  • No Persistence: SSE events are not persisted; disconnected clients miss events
  • Database Backup: All notifications are also stored in the database for 90 days

Best Practices

  1. Deduplication: Check message_id to avoid processing duplicate notifications
  2. Graceful Degradation: If SSE is unavailable, poll the GET notifications endpoint
  3. User Control: Allow users to control notification preferences and types
  4. Visual Feedback: Provide clear visual indication of new vs. read notifications
  5. Persistence: Store notifications locally for offline access
  6. Throttling: Avoid overwhelming users with too many simultaneous notifications

See Also

Gamification Balance Update Event

Info

Draft Documentation - This section is currently under review and may be subject to changes.

The gamification balance update event is sent via SSE when a player’s gamification coin balance changes. This provides real-time updates for balance changes due to earning, redeeming, or other gamification-related activities.

Event Type

event_type: gamification_balance_update

Event Structure

{
  "brand_id": 1001,
  "player_id": 12345,
  "event_type": "gamification_balance_update",
  "event_data": {
    "balance": 1500,
    "total_received": 5000,
    "total_redeemed": 3500
  }
}

Fields

Base Event Fields

Field Type Description
brand_id int The casino brand identifier.
player_id int The player identifier.
event_type string Always “gamification_balance_update” for this event.
event_data object Balance update data (see below).

Event Data Fields

Field Type Description
balance decimal Current gamification coin balance after the update.
total_received decimal Total gamification coins received by the player (lifetime).
total_redeemed decimal Total gamification coins redeemed by the player (lifetime).

Event Examples

Example 1: Balance Update After Earning Coins

This event is sent when a player earns gamification coins (e.g., from gameplay activity).

{
  "brand_id": 1001,
  "player_id": 12345,
  "event_type": "gamification_balance_update",
  "event_data": {
    "balance": 2500,
    "total_received": 10000,
    "total_redeemed": 7500
  }
}

Example 2: Balance Update After Redemption

This event is sent when a player redeems gamification coins for rewards.

{
  "brand_id": 1001,
  "player_id": 12345,
  "event_type": "gamification_balance_update",
  "event_data": {
    "balance": 500,
    "total_received": 10000,
    "total_redeemed": 9500
  }
}

Example 3: New Player with Zero Redemptions

This event shows a player who has earned coins but not yet redeemed any.

{
  "brand_id": 1001,
  "player_id": 12345,
  "event_type": "gamification_balance_update",
  "event_data": {
    "balance": 750,
    "total_received": 750,
    "total_redeemed": 0
  }
}

Client Implementation

JavaScript Example

const eventSource = new EventSource('/gateway/sse/1001/stream', {
  headers: {
    'x-auth-token': playerToken
  }
});

eventSource.onmessage = (event) => {
  const data = JSON.parse(event.data);

  // Filter for gamification balance update events
  if (data.event_type === 'gamification_balance_update') {
    const balanceData = data.event_data;
    updateGamificationBalanceUI(balanceData);
  }
};

function updateGamificationBalanceUI(balanceData) {
  // Update the displayed balance
  document.getElementById('coin-balance').textContent =
    formatNumber(balanceData.balance);

  // Optionally update lifetime stats
  document.getElementById('total-earned').textContent =
    formatNumber(balanceData.total_received);
  document.getElementById('total-redeemed').textContent =
    formatNumber(balanceData.total_redeemed);

  // Show visual feedback for balance change
  highlightBalanceChange();
}

function formatNumber(value) {
  return new Intl.NumberFormat('en-US').format(value);
}

Use Cases

This event is triggered in the following scenarios:

Scenario Description
Coin Earning Player earns coins from gameplay or activities
Coin Redemption Player redeems coins for rewards or bonuses
Admin Adjustment Manual balance adjustment by operator
Promotional Credit Promotional coins added to player’s balance
Expiration Coins expired due to inactivity or time limits

Best Practices

  1. Real-time UI Updates: Update the gamification balance display immediately upon receiving this event
  2. Animation: Consider adding visual feedback (e.g., animation, color change) when the balance updates
  3. Graceful Degradation: If SSE is unavailable, fall back to polling the balance endpoint
  4. Number Handling: Balance values are returned as rounded numbers but should be treated as decimals for storage and calculations

See Also