SimpliRide WebSocket API

Real-time communication API for the SimpliRide ride-hailing platform. Built on Socket.IO with Redis scaling and Firebase persistence.

Authentication

All connections require userId and userType in query parameters.

const socket = io('https://realtime.simpliride.com/trip', { query: { userId: 'user-uuid', userType: 'RIDER' // or 'DRIVER', 'SUPPORT', 'ADMIN' } }); // For drivers, you can also specify vehicle type const driverSocket = io('https://realtime.simpliride.com/driver', { query: { userId: 'driver-uuid', userType: 'DRIVER', vehicleType: 'STANDARD' // SAVER, STANDARD, COMFORT, PRIORITY_PICKUP, XL, PREMIUM, PREMIUM_SUV } });
User Types

Valid user types: RIDER, DRIVER, SUPPORT, ADMIN

Connection Handling

After successful connection, you'll receive a connected event with session details.

connected Listen

Received immediately after successful authentication.

{ "sessionId": "socket-session-id", "userId": "user-uuid", "userType": "RIDER", "timestamp": 1702828800000 }

Error Handling

Errors are emitted on the error event with a code and message.

socket.on('error', (error) => { console.error('Socket error:', error.code, error.message); // error.code: 'RATE_LIMIT', 'FORBIDDEN', 'INVALID_TOKEN', etc. });
Error Code Description Action
RATE_LIMIT Too many requests Wait and retry
FORBIDDEN Action not allowed for user type Check permissions
MISSING_USER_ID userId is required but not provided Provide userId in query
MISSING_USER_TYPE userType is required but not provided Provide userType in query
VALIDATION_ERROR Invalid payload format Fix payload

/trip Namespace

Main namespace for trip lifecycle events. Used by both riders and drivers.

Connection URL

wss://realtime.simpliride.com/trip

trip:subscribe

trip:subscribe Emit

Subscribe to real-time updates for a specific trip.

Request Payload

ParameterTypeRequiredDescription
tripId string (UUID) Required The trip ID to subscribe to

Response

{ "success": true, "tripId": "uuid", "subscribed": true }

Example

socket.emit('trip:subscribe', { tripId: 'trip-uuid' }, (response) => { if (response.success) { console.log('Subscribed to trip updates'); } });

trip:request (Listen)

trip:request Listen

Received by drivers when a new trip request is available.

Payload

{ "tripId": "uuid", "riderId": "uuid", "riderName": "John Doe", "riderRating": 4.8, "pickup": { "latitude": 6.5244, "longitude": 3.3792, "address": "123 Main Street, Lagos" }, "dropoff": { "latitude": 6.4541, "longitude": 3.3947, "address": "456 Victoria Island, Lagos" }, "estimatedFare": 2500.00, "estimatedDistance": 8.5, "estimatedDuration": 25, "vehicleType": "COMFORT", "paymentMethod": "CARD", "expiresAt": 1702828830000, "timestamp": 1702828800000 }

trip:accept

trip:accept Emit

Driver accepts a trip request. Drivers only.

Request Payload

ParameterTypeRequiredDescription
tripId string (UUID) Required Trip ID to accept
latitude number Optional Driver's current latitude
longitude number Optional Driver's current longitude

Example

socket.emit('trip:accept', { tripId: 'trip-uuid', latitude: 6.5244, longitude: 3.3792 }, (response) => { if (response.success) { console.log('Trip accepted!'); } });

trip:accepted (Listen)

trip:accepted Listen

Broadcast to all trip subscribers when driver accepts.

{ "tripId": "uuid", "driverId": "uuid", "driverLocation": { "latitude": 6.5244, "longitude": 3.3792 }, "timestamp": 1702828800000 }

trip:reject

trip:reject Emit

Driver declines a trip request. Drivers only.

ParameterTypeRequiredDescription
tripId string (UUID) Required Trip ID to reject
reason string Optional Reason for rejection

trip:cancel

trip:cancel Emit

Cancel an active trip. Can be used by both riders and drivers.

ParameterTypeRequiredDescription
tripId string (UUID) Required Trip ID to cancel
reason string Optional Cancellation reason

trip:status (Listen)

trip:status Listen

Received when trip status changes.

{ "tripId": "uuid", "status": "DRIVER_EN_ROUTE", "previousStatus": "DRIVER_ASSIGNED", "driverId": "uuid", "timestamp": 1702828800000 }

Trip Status Values

StatusDescription
PENDINGTrip created, searching for drivers
DRIVER_ASSIGNEDDriver accepted the trip
DRIVER_EN_ROUTEDriver heading to pickup
DRIVER_ARRIVEDDriver at pickup location
TRIP_STARTEDRider picked up, trip in progress
COMPLETEDTrip completed
CANCELLEDTrip cancelled

trip:eta (Listen)

trip:eta Listen

ETA updates during the trip.

{ "tripId": "uuid", "etaMinutes": 5, "etaSeconds": 320, "distanceMeters": 2500, "trafficCondition": "MODERATE", "timestamp": 1702828800000 }

Emergency SOS

emergency:trigger Emit

Trigger an emergency SOS. Available to both riders and drivers.

ParameterTypeRequiredDescription
tripId string (UUID) Required Trip ID
type string Required SOS, ACCIDENT, HARASSMENT, MEDICAL, OTHER
latitude number Optional Current latitude
longitude number Optional Current longitude
description string Optional Additional details
support:join Emit

Support agent joins an emergency to monitor the trip. Support/Admin only.

ParameterTypeRequiredDescription
tripId string (UUID) Required Trip ID with active emergency
socket.emit('support:join', { tripId: 'trip-uuid' });
support:leave Emit

Support agent leaves emergency monitoring. Support/Admin only.

ParameterTypeRequiredDescription
tripId string (UUID) Required Trip ID to stop monitoring
socket.emit('support:leave', { tripId: 'trip-uuid' });

Trip Sharing

tripShare:subscribe Emit

Subscribe to shared trip updates (for emergency contacts viewing shared trip).

// No authentication required for shared trip viewing socket.emit('tripShare:subscribe', { shareToken: 'share-token-from-link' }); // Listen for updates socket.on('tripShare:update', (update) => { console.log('Shared trip update:', update); });
tripShare:unsubscribe Emit

Unsubscribe from shared trip updates.

ParameterTypeRequiredDescription
shareToken string Required Share token from the shared link
socket.emit('tripShare:unsubscribe', { shareToken: 'share-token' });

/driver Namespace

Driver-specific namespace for location updates, availability, and trip notifications.

Connection URL

wss://realtime.simpliride.com/driver
Driver Only

This namespace only accepts connections with userType: 'DRIVER'

location:update

location:update Emit

Update driver's current location. Should be sent every 3-5 seconds when online.

ParameterTypeRequiredDescription
latitude number Required Current latitude (-90 to 90)
longitude number Required Current longitude (-180 to 180)
speed number Optional Speed in km/h
heading number Optional Heading in degrees (0-360)
accuracy number Optional GPS accuracy in meters
tripId string Optional If on active trip, broadcasts to rider

Example

socket.emit('location:update', { latitude: 6.5244, longitude: 3.3792, speed: 45.5, heading: 180, accuracy: 10, tripId: 'active-trip-uuid' // optional });

availability:update

availability:update Emit

Toggle driver availability to receive trip requests.

ParameterTypeRequiredDescription
available boolean Required true = online, false = offline
latitude number Required Current latitude
longitude number Required Current longitude
vehicleType string Optional SAVER, STANDARD, COMFORT, PRIORITY_PICKUP, XL, PREMIUM, PREMIUM_SUV

Example

// Go online socket.emit('availability:update', { available: true, latitude: 6.5244, longitude: 3.3792, vehicleType: 'COMFORT' }); // Go offline socket.emit('availability:update', { available: false, latitude: 6.5244, longitude: 3.3792 });

vehicle:update

vehicle:update Emit

Change active vehicle type.

socket.emit('vehicle:update', { vehicleType: 'PREMIUM' });

Driver Trip Events

trip:arrived:pickup Emit

Notify rider that driver has arrived at pickup.

socket.emit('trip:arrived:pickup', { tripId: 'trip-uuid' });
trip:started Emit

Mark trip as started after rider boards.

socket.emit('trip:started', { tripId: 'trip-uuid' });
trip:completed Emit

Mark trip as completed.

socket.emit('trip:completed', { tripId: 'trip-uuid', finalFare: 2500.00 });

driver:location (Listen - for Riders)

driver:location Listen

Riders receive driver location updates when subscribed to a trip.

{ "driverId": "uuid", "latitude": 6.5244, "longitude": 3.3792, "speed": 45.5, "heading": 180, "timestamp": 1702828800000 }

/chat Namespace

In-trip messaging between rider and driver.

Connection URL

wss://realtime.simpliride.com/chat

chat:join

chat:join Emit

Join a trip's chat room.

socket.emit('chat:join', { tripId: 'trip-uuid' });

chat:message

chat:message Emit

Send a chat message.

ParameterTypeRequiredDescription
tripId string Required Trip ID
message string Required Message content (max 500 chars)
messageType string Optional text (default), location, image

chat:message (Listen)

chat:message Listen

Receive chat messages.

{ "id": "message-uuid", "tripId": "trip-uuid", "senderId": "user-uuid", "senderType": "RIDER", "message": "I'm at the blue gate", "messageType": "text", "timestamp": 1702828800000 }

chat:typing

chat:typing Emit

Send typing indicator.

socket.emit('chat:typing', { tripId: 'trip-uuid', isTyping: true });

chat:history

chat:history Emit

Get chat history for a trip.

socket.emit('chat:history', { tripId: 'trip-uuid', limit: 50 }, (response) => { console.log('Chat history:', response.messages); });

chat:read

chat:read Emit

Mark a message as read and notify the sender.

ParameterTypeRequiredDescription
tripId string Required Trip ID
messageId string Required Message ID to mark as read
socket.emit('chat:read', { tripId: 'trip-uuid', messageId: 'msg-uuid' });

chat:read (Listen)

chat:read Listen

Received when other party reads a message.

{ "tripId": "trip-uuid", "messageId": "msg-uuid", "readBy": "user-uuid", "timestamp": 1702828800000 }

/voice Namespace

WebRTC-based voice calling between rider and driver during active trips. The gateway acts as a signaling server while actual audio is peer-to-peer.

WebRTC Voice Calling

Voice calls use WebRTC for peer-to-peer audio. The gateway handles signaling (offer/answer/ICE candidates) while audio streams directly between devices.

Connection URL

wss://realtime.simpliride.com/voice

voice:getIceServers

voice:getIceServers Emit

Get ICE server configuration for WebRTC. Call this before initiating a call to get STUN/TURN servers.

Response

{ "iceServers": [ { "urls": "stun:stun.l.google.com:19302" }, { "urls": "stun:stun1.l.google.com:19302" }, { "urls": "turn:turn.example.com:3478", "username": "user", "credential": "pass" } ] }

Example

socket.emit('voice:getIceServers', {}, (response) => { const peerConnection = new RTCPeerConnection({ iceServers: response.iceServers }); });

voice:initiate

voice:initiate Emit

Start a voice call with the other participant in an active trip.

ParameterTypeRequiredDescription
tripId string (UUID) Required Active trip ID

Response

{ "success": true, "callId": "call-uuid", "calleeId": "other-user-uuid", "calleeType": "DRIVER" }

Error Codes

CodeDescription
USER_BUSYUser is already in another call
CALLEE_BUSYOther participant is in another call
CALLEE_OFFLINEOther participant is not connected
NOT_TRIP_PARTICIPANTYou are not part of this trip
VOICE_DISABLEDVoice calling is disabled

Example

// 1. Get ICE servers socket.emit('voice:getIceServers', {}, async (iceResponse) => { // 2. Create peer connection const pc = new RTCPeerConnection({ iceServers: iceResponse.iceServers }); // 3. Add local audio track const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); stream.getTracks().forEach(track => pc.addTrack(track, stream)); // 4. Initiate call socket.emit('voice:initiate', { tripId: 'trip-uuid' }, async (response) => { if (response.success) { const callId = response.callId; // 5. Create and send offer const offer = await pc.createOffer(); await pc.setLocalDescription(offer); socket.emit('voice:offer', { callId, tripId: 'trip-uuid', offer }); } }); });

voice:incoming (Listen)

voice:incoming Listen

Received when someone is calling you.

{ "callId": "call-uuid", "tripId": "trip-uuid", "callerId": "caller-uuid", "callerType": "RIDER", "timestamp": 1702828800000 }

Example

socket.on('voice:incoming', (data) => { // Show incoming call UI showIncomingCallDialog({ callId: data.callId, callerType: data.callerType, onAccept: () => acceptCall(data.callId, data.tripId), onReject: () => rejectCall(data.callId, data.tripId) }); });

voice:accept

voice:accept Emit

Accept an incoming call.

ParameterTypeRequiredDescription
callId string (UUID) Required Call ID from voice:incoming
tripId string (UUID) Required Trip ID

Response

{ "success": true, "callId": "call-uuid" }

voice:accepted (Listen)

voice:accepted Listen

Received by caller when call is accepted.

{ "callId": "call-uuid", "tripId": "trip-uuid", "acceptedBy": "callee-uuid", "timestamp": 1702828800000 }

voice:reject

voice:reject Emit

Reject an incoming call.

ParameterTypeRequiredDescription
callId string (UUID) Required Call ID
tripId string (UUID) Required Trip ID
reason string Optional Rejection reason

voice:rejected (Listen)

voice:rejected Listen

Received by caller when call is rejected.

{ "callId": "call-uuid", "tripId": "trip-uuid", "rejectedBy": "callee-uuid", "reason": "BUSY", "timestamp": 1702828800000 }

voice:end

voice:end Emit

End an active call.

ParameterTypeRequiredDescription
callId string (UUID) Required Call ID
tripId string (UUID) Required Trip ID
reason string Optional NORMAL, CANCELLED, NETWORK_ERROR

voice:ended (Listen)

voice:ended Listen

Received when call ends.

{ "callId": "call-uuid", "tripId": "trip-uuid", "endedBy": "user-uuid", "reason": "NORMAL", "duration": 125, "timestamp": 1702828800000 }

End Reasons

ReasonDescription
NORMALCall ended normally
REJECTEDCall was rejected
BUSYCallee was busy
NO_ANSWERNo answer (timeout)
FAILEDConnection failed
CANCELLEDCaller cancelled
NETWORK_ERRORNetwork issue

voice:missed (Listen)

voice:missed Listen

Received when call times out (30 seconds without answer).

{ "callId": "call-uuid", "tripId": "trip-uuid", "timestamp": 1702828800000 }

WebRTC Signaling Events

These events relay WebRTC signaling data between peers.

voice:offer

voice:offer Emit

Send WebRTC offer (SDP) to callee. Sent by caller after initiating call.

ParameterTypeRequiredDescription
callId string Required Call ID
tripId string Required Trip ID
offer RTCSessionDescription Required WebRTC offer SDP

voice:offer (Listen)

voice:offer Listen

Received by callee with the WebRTC offer.

{ "callId": "call-uuid", "tripId": "trip-uuid", "offer": { "type": "offer", "sdp": "v=0\r\no=- 46117..." }, "timestamp": 1702828800000 }

Handling Offer

socket.on('voice:offer', async (data) => { // Set remote description await peerConnection.setRemoteDescription(new RTCSessionDescription(data.offer)); // Create answer const answer = await peerConnection.createAnswer(); await peerConnection.setLocalDescription(answer); // Send answer back socket.emit('voice:answer', { callId: data.callId, tripId: data.tripId, answer: answer }); });

voice:answer

voice:answer Emit

Send WebRTC answer (SDP) to caller. Sent by callee after receiving offer.

ParameterTypeRequiredDescription
callId string Required Call ID
tripId string Required Trip ID
answer RTCSessionDescription Required WebRTC answer SDP

voice:answer (Listen)

voice:answer Listen

Received by caller with the WebRTC answer.

socket.on('voice:answer', async (data) => { await peerConnection.setRemoteDescription( new RTCSessionDescription(data.answer) ); // Connection should now establish });

voice:ice-candidate

voice:ice-candidate Emit

Send ICE candidate to peer. Called for each candidate discovered.

ParameterTypeRequiredDescription
callId string Required Call ID
tripId string Required Trip ID
candidate RTCIceCandidate Required ICE candidate object

Example

// Send ICE candidates as they're discovered peerConnection.onicecandidate = (event) => { if (event.candidate) { socket.emit('voice:ice-candidate', { callId: callId, tripId: tripId, candidate: event.candidate }); } };

voice:ice-candidate (Listen)

voice:ice-candidate Listen

Received ICE candidate from peer.

socket.on('voice:ice-candidate', async (data) => { if (data.candidate) { await peerConnection.addIceCandidate( new RTCIceCandidate(data.candidate) ); } });

Additional Voice Events

voice:mute

voice:mute Emit

Toggle mute state and notify the other participant.

ParameterTypeRequiredDescription
callId string Required Call ID
tripId string Required Trip ID
muted boolean Required Mute state

voice:muted (Listen)

voice:muted Listen

Received when other participant toggles mute.

{ "callId": "call-uuid", "tripId": "trip-uuid", "userId": "user-uuid", "muted": true, "timestamp": 1702828800000 }

voice:busy (Listen)

voice:busy Listen

Received when the callee is already in another call.

{ "callId": "call-uuid", "tripId": "trip-uuid", "calleeId": "user-uuid", "timestamp": 1702828800000 }

voice:history

voice:history Emit

Get call history for a trip.

ParameterTypeRequiredDescription
tripId string Required Trip ID
limit number Optional Max results (default: 10)

Response

{ "calls": [ { "callId": "call-uuid", "callerId": "user-uuid", "callerType": "RIDER", "calleeId": "driver-uuid", "calleeType": "DRIVER", "state": "ENDED", "duration": 125, "endReason": "NORMAL", "initiatedAt": 1702828600000, "endedAt": 1702828725000 } ], "tripId": "trip-uuid" }

Complete Voice Call Flow Example

// === CALLER SIDE === class VoiceCaller { constructor(socket, tripId) { this.socket = socket; this.tripId = tripId; this.peerConnection = null; this.callId = null; } async startCall() { // 1. Get ICE servers const iceServers = await new Promise(resolve => { this.socket.emit('voice:getIceServers', {}, resolve); }); // 2. Create peer connection this.peerConnection = new RTCPeerConnection({ iceServers: iceServers.iceServers }); // 3. Set up ICE candidate handling this.peerConnection.onicecandidate = (event) => { if (event.candidate) { this.socket.emit('voice:ice-candidate', { callId: this.callId, tripId: this.tripId, candidate: event.candidate }); } }; // 4. Handle incoming audio this.peerConnection.ontrack = (event) => { const remoteAudio = document.getElementById('remoteAudio'); remoteAudio.srcObject = event.streams[0]; }; // 5. Add local audio const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); stream.getTracks().forEach(track => { this.peerConnection.addTrack(track, stream); }); // 6. Initiate call const response = await new Promise(resolve => { this.socket.emit('voice:initiate', { tripId: this.tripId }, resolve); }); if (!response.success) { throw new Error(response.error); } this.callId = response.callId; // 7. Create and send offer const offer = await this.peerConnection.createOffer(); await this.peerConnection.setLocalDescription(offer); this.socket.emit('voice:offer', { callId: this.callId, tripId: this.tripId, offer: offer }); // 8. Listen for answer this.socket.on('voice:answer', async (data) => { await this.peerConnection.setRemoteDescription( new RTCSessionDescription(data.answer) ); }); // 9. Listen for ICE candidates this.socket.on('voice:ice-candidate', async (data) => { if (data.candidate) { await this.peerConnection.addIceCandidate( new RTCIceCandidate(data.candidate) ); } }); } endCall() { this.socket.emit('voice:end', { callId: this.callId, tripId: this.tripId, reason: 'NORMAL' }); this.peerConnection?.close(); } } // === CALLEE SIDE === class VoiceCallee { constructor(socket) { this.socket = socket; this.peerConnection = null; // Listen for incoming calls socket.on('voice:incoming', (data) => { this.handleIncomingCall(data); }); } async handleIncomingCall(data) { // Show UI and wait for user action const accepted = await showIncomingCallUI(data); if (!accepted) { this.socket.emit('voice:reject', { callId: data.callId, tripId: data.tripId }); return; } // Accept call this.socket.emit('voice:accept', { callId: data.callId, tripId: data.tripId }); // Set up peer connection (similar to caller) // ...handle voice:offer, create answer, exchange ICE candidates } }

/support Namespace

Real-time support chat for users (riders/drivers) to communicate with admin support staff. Messages are persisted in Firebase and synced with the admin service.

Connection URL

wss://realtime.simpliride.com/support
User Types

Support chat is available for RIDER and DRIVER user types.

support:create-conversation

support:create-conversation Emit

Create a new support conversation.

Request Payload

ParameterTypeRequiredDescription
subject string Required Subject/title of the conversation
priority string Optional Priority: low, normal, high, urgent (default: normal)
relatedTripId string (UUID) Optional Related trip ID if applicable

Response

{ "success": true, "conversationId": "uuid", "message": "Conversation created" }

Example

socket.emit('support:create-conversation', { subject: 'Issue with my last trip', priority: 'normal', relatedTripId: 'trip-uuid' }, (response) => { if (response.success) { console.log('Conversation created:', response.conversationId); } });

support:join

support:join Emit

Join a conversation room to receive real-time updates.

ParameterTypeRequiredDescription
conversationId string (UUID) Required The conversation ID to join
socket.emit('support:join', { conversationId: 'conv-uuid' });

support:message

support:message Emit

Send a message in a conversation.

Request Payload

ParameterTypeRequiredDescription
conversationId string (UUID) Required The conversation ID
content string Required Message content
attachmentUrl string Optional URL of attached file
attachmentType string Optional Type: image, document, audio
replyToMessageId string (UUID) Optional ID of message being replied to

Example

socket.emit('support:message', { conversationId: 'conv-uuid', content: 'Hello, I need help with my trip' }, (response) => { if (response.success) { console.log('Message sent:', response.messageId); } });

support:message (Listen)

support:message Listen

Received when a new message arrives from admin.

{ "messageId": "uuid", "conversationId": "uuid", "senderId": "admin-uuid", "senderType": "ADMIN", "senderName": "Support Agent", "content": "Hello, how can I help you?", "attachmentUrl": null, "attachmentType": null, "timestamp": 1702828800000 }

support:typing

support:typing Emit

Send typing indicator.

ParameterTypeRequiredDescription
conversationId string (UUID) Required The conversation ID
isTyping boolean Required Whether user is typing
socket.emit('support:typing', { conversationId: 'conv-uuid', isTyping: true });

support:typing (Listen)

support:typing Listen

Received when admin is typing.

{ "conversationId": "uuid", "adminId": "admin-uuid", "adminName": "Support Agent", "isTyping": true }

support:status (Listen)

support:status Listen

Received when conversation status changes.

{ "conversationId": "uuid", "status": "RESOLVED", "resolvedAt": "2024-01-15T10:30:00Z", "timestamp": 1702828800000 }

Status Values

StatusDescription
OPENNew conversation, not yet assigned
IN_PROGRESSAssigned to admin and being handled
RESOLVEDIssue resolved, awaiting feedback
CLOSEDConversation closed

support:assigned (Listen)

support:assigned Listen

Received when an admin is assigned to the conversation.

{ "conversationId": "uuid", "adminId": "admin-uuid", "adminName": "Support Agent", "timestamp": 1702828800000 }

support:history

support:history Emit

Get message history for a conversation.

ParameterTypeRequiredDescription
conversationId string (UUID) Required The conversation ID
limit number Optional Max messages (default: 50)

Response

{ "success": true, "messages": [ { "messageId": "uuid", "senderId": "user-uuid", "senderType": "USER", "content": "Hello", "timestamp": 1702828800000 } ], "hasMore": false }

support:conversations

support:conversations Emit

Get list of user's support conversations.

ParameterTypeRequiredDescription
limit number Optional Max conversations (default: 20)

Response

{ "success": true, "conversations": [ { "conversationId": "uuid", "subject": "Trip Issue", "status": "IN_PROGRESS", "priority": "NORMAL", "assignedAdminName": "Support Agent", "lastMessageAt": "2024-01-15T10:30:00Z", "unreadCount": 2 } ] }

Admin Internal API

The admin service communicates with the realtime gateway via HTTP to send messages and manage conversations in real-time.

Internal API

These endpoints are for admin-service to realtime-gateway communication only. They require the x-internal-api-key header.

Base URL

/api/v1/internal/support-chat

Available Endpoints

MethodEndpointDescription
POST /conversations/{id}/messages Send message from admin to user
POST /conversations/{id}/status Update conversation status
POST /conversations/{id}/assign Assign admin to conversation
POST /conversations/{id}/typing Send admin typing indicator
POST /conversations/{id}/read Mark messages as read
POST /conversations/sync Sync conversation to Firebase
POST /conversations/{id}/messages/fetch Fetch messages from Firebase

Complete Support Chat Example

// Connect to support namespace const socket = io('https://realtime.simpliride.com/support', { query: { userId: 'user-uuid', userType: 'RIDER' }, transports: ['websocket'] }); let currentConversationId = null; // Create a new support conversation function createConversation(subject, relatedTripId = null) { socket.emit('support:create-conversation', { subject, priority: 'normal', relatedTripId }, (response) => { if (response.success) { currentConversationId = response.conversationId; joinConversation(currentConversationId); } }); } // Join conversation room function joinConversation(conversationId) { socket.emit('support:join', { conversationId }, (response) => { if (response.success) { loadHistory(conversationId); } }); } // Load message history function loadHistory(conversationId) { socket.emit('support:history', { conversationId, limit: 50 }, (response) => { if (response.success) { displayMessages(response.messages); } }); } // Send a message function sendMessage(content) { socket.emit('support:message', { conversationId: currentConversationId, content }, (response) => { if (response.success) { console.log('Message sent:', response.messageId); } }); } // Listen for incoming messages socket.on('support:message', (message) => { if (message.conversationId === currentConversationId) { displayNewMessage(message); socket.emit('support:read', { conversationId: currentConversationId }); } }); // Listen for admin typing socket.on('support:typing', (data) => { if (data.conversationId === currentConversationId) { showTypingIndicator(data.adminName, data.isTyping); } }); // Listen for status changes socket.on('support:status', (data) => { if (data.conversationId === currentConversationId) { updateConversationStatus(data.status); } }); // Listen for admin assignment socket.on('support:assigned', (data) => { if (data.conversationId === currentConversationId) { showNotification(`${data.adminName} is now handling your request`); } });

Platform Integration Guides

Android (Kotlin)

// build.gradle implementation 'io.socket:socket.io-client:2.1.0' // Usage import io.socket.client.IO import io.socket.client.Socket import org.json.JSONObject class SocketManager { private lateinit var socket: Socket fun connect(userId: String, userType: String) { val options = IO.Options().apply { query = "userId=$userId&userType=$userType" transports = arrayOf("websocket") reconnection = true reconnectionAttempts = 5 reconnectionDelay = 1000 } socket = IO.socket("https://realtime.simpliride.com/trip", options) socket.on(Socket.EVENT_CONNECT) { Log.d("Socket", "Connected!") } socket.on("trip:request") { args -> val tripRequest = args[0] as JSONObject // Handle new trip request } socket.on("trip:status") { args -> val status = args[0] as JSONObject // Handle status update } socket.connect() } fun acceptTrip(tripId: String, lat: Double, lng: Double) { val data = JSONObject().apply { put("tripId", tripId) put("latitude", lat) put("longitude", lng) } socket.emit("trip:accept", data) { response -> val result = response[0] as JSONObject if (result.getBoolean("success")) { // Trip accepted } } } fun updateLocation(lat: Double, lng: Double, speed: Float?, tripId: String?) { val data = JSONObject().apply { put("latitude", lat) put("longitude", lng) speed?.let { put("speed", it) } tripId?.let { put("tripId", it) } } socket.emit("location:update", data) } fun disconnect() { socket.disconnect() } }

iOS (Swift)

// Package.swift or CocoaPods // pod 'Socket.IO-Client-Swift', '~> 16.0' import SocketIO class SocketManager { private var manager: SocketManager! private var socket: SocketIOClient! func connect(userId: String, userType: String) { manager = SocketManager( socketURL: URL(string: "https://realtime.simpliride.com")!, config: [ .log(true), .compress, .connectParams([ "userId": userId, "userType": userType ]) ] ) socket = manager.socket(forNamespace: "/trip") socket.on(clientEvent: .connect) { data, ack in print("Connected!") } socket.on("trip:request") { [weak self] data, ack in guard let tripRequest = data[0] as? [String: Any] else { return } // Handle new trip request } socket.on("trip:status") { [weak self] data, ack in guard let status = data[0] as? [String: Any] else { return } // Handle status update } socket.connect() } func acceptTrip(tripId: String, location: CLLocationCoordinate2D) { let data: [String: Any] = [ "tripId": tripId, "latitude": location.latitude, "longitude": location.longitude ] socket.emitWithAck("trip:accept", data).timingOut(after: 10) { response in if let result = response[0] as? [String: Any], let success = result["success"] as? Bool, success { // Trip accepted } } } func updateLocation(_ location: CLLocation, tripId: String?) { var data: [String: Any] = [ "latitude": location.coordinate.latitude, "longitude": location.coordinate.longitude, "speed": max(0, location.speed * 3.6), // m/s to km/h "heading": location.course ] if let tripId = tripId { data["tripId"] = tripId } socket.emit("location:update", data) } func disconnect() { socket.disconnect() } }

Web (JavaScript/TypeScript)

// npm install socket.io-client import { io, Socket } from 'socket.io-client'; interface TripRequest { tripId: string; riderId: string; riderName: string; pickup: { latitude: number; longitude: number; address: string }; dropoff: { latitude: number; longitude: number; address: string }; estimatedFare: number; } class SimpliRideSocket { private socket: Socket | null = null; connect(userId: string, userType: 'RIDER' | 'DRIVER'): void { this.socket = io('https://realtime.simpliride.com/trip', { query: { userId, userType }, transports: ['websocket'], reconnection: true, reconnectionAttempts: 5, reconnectionDelay: 1000, }); this.socket.on('connect', () => { console.log('Connected to SimpliRide Gateway'); }); this.socket.on('connected', (data) => { console.log('Session established:', data); }); this.socket.on('trip:request', (trip: TripRequest) => { console.log('New trip request:', trip); // Show trip to driver }); this.socket.on('trip:status', (status) => { console.log('Trip status update:', status); // Update UI }); this.socket.on('trip:eta', (eta) => { console.log('ETA update:', eta); }); this.socket.on('driver:location', (location) => { console.log('Driver location:', location); // Update map marker }); this.socket.on('error', (error) => { console.error('Socket error:', error); }); this.socket.on('disconnect', (reason) => { console.log('Disconnected:', reason); }); } subscribeToTrip(tripId: string): Promise<boolean> { return new Promise((resolve) => { this.socket?.emit('trip:subscribe', { tripId }, (response: any) => { resolve(response.success); }); }); } acceptTrip(tripId: string, latitude?: number, longitude?: number): Promise<any> { return new Promise((resolve, reject) => { this.socket?.emit('trip:accept', { tripId, latitude, longitude }, (response: any) => { if (response.success) { resolve(response); } else { reject(new Error(response.error || 'Failed to accept trip')); } }); }); } cancelTrip(tripId: string, reason?: string): void { this.socket?.emit('trip:cancel', { tripId, reason }); } triggerEmergency(tripId: string, type: string, latitude?: number, longitude?: number): void { this.socket?.emit('emergency:trigger', { tripId, type, latitude, longitude }); } disconnect(): void { this.socket?.disconnect(); this.socket = null; } } // Usage const socket = new SimpliRideSocket(); socket.connect('user-id', 'RIDER');

Flutter (Dart)

// pubspec.yaml // socket_io_client: ^2.0.3 import 'package:socket_io_client/socket_io_client.dart' as IO; class SocketService { late IO.Socket socket; void connect(String userId, String userType) { socket = IO.io( 'https://realtime.simpliride.com/trip', IO.OptionBuilder() .setTransports(['websocket']) .setQuery({'userId': userId, 'userType': userType}) .enableReconnection() .setReconnectionAttempts(5) .setReconnectionDelay(1000) .build(), ); socket.onConnect((_) { print('Connected to SimpliRide Gateway'); }); socket.on('connected', (data) { print('Session: $data'); }); socket.on('trip:request', (data) { print('New trip request: $data'); // Handle trip request }); socket.on('trip:status', (data) { print('Status update: $data'); }); socket.on('driver:location', (data) { print('Driver location: $data'); // Update map }); socket.onError((error) { print('Socket error: $error'); }); socket.onDisconnect((_) { print('Disconnected'); }); socket.connect(); } Future<bool> subscribeToTrip(String tripId) async { final completer = Completer<bool>(); socket.emitWithAck('trip:subscribe', {'tripId': tripId}, ack: (response) { completer.complete(response['success'] ?? false); }); return completer.future; } void acceptTrip(String tripId, double lat, double lng) { socket.emitWithAck('trip:accept', { 'tripId': tripId, 'latitude': lat, 'longitude': lng, }, ack: (response) { if (response['success']) { print('Trip accepted!'); } }); } void updateLocation(double lat, double lng, {double? speed, String? tripId}) { socket.emit('location:update', { 'latitude': lat, 'longitude': lng, if (speed != null) 'speed': speed, if (tripId != null) 'tripId': tripId, }); } void sendMessage(String tripId, String message) { socket.emit('chat:message', { 'tripId': tripId, 'message': message, }); } void disconnect() { socket.disconnect(); } }

Need Help?

Try out events in real-time with our interactive test console.

Open Test Console