RideWiseCore Docs

Real-time events

The Socket.IO connection for agent locations, ride offers and live updates.

Tracking and the offer/response cycle run over a Socket.IO (v4) connection to the same host as the REST API. Use any Socket.IO v4 client: JavaScript/TypeScript, Python, Java, Swift and others.

Connect from your backend only. The socket authenticates with your API key, which must never ship inside an agent or passenger app. Your backend relays events between RideWiseCore and your apps.

Connecting

import { io } from "socket.io-client";

const socket = io(process.env.RWC_API_URL!, {
  auth: { apiKey: process.env.RWC_API_KEY },
});

socket.on("connect", () => console.log("connected", socket.id));
socket.on("connect_error", (err) => console.error("refused:", err.message));
socket.on("disconnect", (reason) => console.warn("disconnected:", reason));

Handshake errors

If the handshake fails, you receive connect_error with one of these messages:

err.messageFix
Missing API keyPass auth: { apiKey }
Invalid or revoked API keyUse an active key
A payment method must be on file before matching or tracking is availableAdd a payment method in the dashboard
The free trial ended on YYYY-MM-DD — …Add a payment method in the dashboard. See Free trial
This tenant has cancelled their subscription — …Subscription cancelled
Payment could not be processed after two attempts — …Suspended for non-payment. See Billing

Socket.IO reconnects automatically after network drops. Authentication errors aren't retried usefully, so fix the cause and reconnect.

Rooms and fan-out

Every socket authenticated with one of your keys joins your private tenant room. Every server event below goes to all sockets in that room, so:

  • You can run several backend instances, each with its own socket. They all receive every event.
  • Only one instance should act on each ride:offer. Deliver it to the agent once, for example by routing on agentId or using a shared lock in your own system.

Events you send

agent:location

Report an agent's position and status. Send this every 3–10 seconds for each online agent. See Agent tracking.

socket.emit(
  "agent:location",
  {
    agentId: "agt_1001",
    lat: 40.7128,
    lng: -74.0060,
    status: "available",
    vehicleType: "sedan",
    heading: 92,
    speed: 11.4,
  },
  (ack) => { /* { ok: true } or { ok: false, error } */ },
);

Prop

Type

ride:respond

Relay an agent's answer to a ride:offer.

socket.emit(
  "ride:respond",
  { matchId: "9f1c2d4e-…", agentId: "agt_1001", response: "accept" },
  (ack) => {
    if (!ack.ok) {
      // "Offer is no longer active": it expired, was cancelled, or was already answered.
      // Tell the agent the ride is gone.
    }
  },
);

Prop

Type

Acknowledgements

Both events accept an optional acknowledgement callback, which RideWiseCore calls with:

type Ack = { ok: true } | { ok: false; error: string };

Always pass one when you need to know the outcome, especially for ride:respond.

Events you receive

ride:offer

An agent has been selected and reserved for a match. Deliver the offer to that agent's app. They have expiresInMs to answer before the offer moves on.

{
  "matchId": "9f1c2d4e-6b7a-4c3d-8e9f-0a1b2c3d4e5f",
  "agentId": "agt_1001",
  "pickup": { "lat": 40.713, "lng": -74.0055 },
  "vehicleType": "sedan",
  "expiresInMs": 15000
}

Show the agent a countdown slightly shorter than expiresInMs to allow for network delay between RideWiseCore, your backend and the agent app.

match:update

A match reached a final state. The payload is the full match object.

statusWhat to do
matchedAssign matchedAgentId to the trip in your system and notify the passenger
unmatchedTell the passenger no agent is available, or retry with a larger radius
cancelledConfirmation of your own cancel

agent:update

Echoed to your room every time a agent:location is stored. Use it to drive live maps in your dashboards or passenger apps.

{
  "agentId": "agt_1001",
  "lat": 40.7128,
  "lng": -74.0060,
  "status": "available",
  "vehicleType": "sedan",
  "heading": 92,
  "speed": 11.4,
  "updatedAt": "2026-09-23T08:15:04.502Z"
}

RideWiseCore changes some statuses itself, for example setting an agent to busy when they accept, or offline when they go silent. It doesn't emit a agent:update for those changes. Use match:update for accepts, and treat an agent as gone when you stop sending their locations.

TypeScript types

Copy these into your project for a fully typed client:

rwc-events.ts
import type { Socket } from "socket.io-client";

export type AgentStatus = "available" | "busy" | "offline";
export type Ack = { ok: true } | { ok: false; error: string };

export interface AgentLocation {
  agentId: string;
  lat: number;
  lng: number;
  status?: AgentStatus;
  vehicleType: string;
  heading?: number;
  speed?: number;
}

export interface AgentRecord extends Required<Pick<AgentLocation, "agentId" | "lat" | "lng" | "vehicleType">> {
  status: AgentStatus;
  heading?: number;
  speed?: number;
  updatedAt: string;
}

export interface RideOffer {
  matchId: string;
  agentId: string;
  pickup: { lat: number; lng: number };
  vehicleType: string;
  expiresInMs: number;
}

export interface Match {
  id: string;
  tenantId: string;
  pickup: { lat: number; lng: number };
  vehicleType: string;
  radiusMeters: number;
  status: "searching" | "matched" | "unmatched" | "cancelled";
  currentCandidateAgentId: string | null;
  matchedAgentId: string | null;
  triedAgentIds: string[];
  createdAt: string;
  updatedAt: string;
}

export interface ServerToClientEvents {
  "ride:offer": (offer: RideOffer) => void;
  "match:update": (match: Match) => void;
  "agent:update": (agent: AgentRecord) => void;
}

export interface ClientToServerEvents {
  "agent:location": (payload: AgentLocation, ack?: (result: Ack) => void) => void;
  "ride:respond": (
    payload: { matchId: string; agentId: string; response: "accept" | "decline" },
    ack?: (result: Ack) => void,
  ) => void;
}

export type RwcSocket = Socket<ServerToClientEvents, ClientToServerEvents>;

On this page