Server-Sent Events
The SSE v2 endpoint streams every case, tour, and activity event in real time over /api/v2/events/stream. All events are delivered through a single authenticated Server-Sent Events connection with tenant-aware filtering.
Endpoint snapshotβ
GET /api/v2/events/stream- Requires
Authorization: Bearer <JWT> - Response media type is
text/event-stream - Optional query parameter:
lastEventIdβ resumes the stream from a cached event ID (see replay section) - Status codes:
200β live SSE stream (eachevent:is a specificBaseEventDTOsubtype)401β unauthenticated (ErrorDTO)429β more than the allowed number of concurrent SSE connections per user (default5)500β generic server failure
Making a connectionβ
import { fetchEventSource } from '@microsoft/fetch-event-source';
await fetchEventSource('https://api.tourfold.com/api/v2/events/stream', {
headers: { Authorization: `Bearer ${token}` },
onmessage(event) {
console.log('event received', event.event, JSON.parse(event.data));
},
});
Resuming with lastEventIdβ
All events include an id: header that mirrors eventId. Persist the latest one client-side and send it on reconnect to replay missed data from the in-memory history (roughly the last 10β―000 events per node). Assuming you cached the previous ID in lastEventId:
await fetchEventSource(
`https://api.tourfold.com/api/v2/events/stream?lastEventId=${encodeURIComponent(lastEventId ?? '')}`,
{ headers: { Authorization: `Bearer ${token}` }, onmessage: handleEvent }
);
No error given when a wrong lastEventId is provided or an eventId old enough to not be present in the memory any longer.
Only the last 100 events are preserved and can be continued from, as means of dealing with short network disturbances.
This API does not guarantee that no events will be missed while the consumer is disconnected.
Event formatβ
Every SSE message looks like the following:
event: CASE_CREATED:V1
id: 018fb1ba-15c1-7f2a-9f2d-2f2b0b8f3b1a
data: {"kind":"CASE_CREATED:V1","eventId":"018fb1ba-15c1-7f2a-9f2d-2f2b0b8f3b1a","timestamp":"2024-03-22T10:41:09.123Z","tenantId":"...","caseId":"...","caseData":{...}}
Common envelope fields:
eventId/id:β UUIDv7 generated server-side for ordering.timestampβ the moment the backend recorded the change (UTC).tenantIdβ always matches the subscriberβs tenant; other tenantsβ events are filtered.businessIdβ convenience string derived fromcaseId,tourId,vehicleId, etc.kindβ discriminator and SSEevent:name.
Event kindsβ
| Event kind | When it fires |
|---|---|
CASE_CREATED:V1 | Case persisted for the first time |
CASE_UPDATED:V1 | Any update to an existing case |
CASE_ACTION:V1 <strong style={{color:'#c62828'}}>DEPRECATED | Legacy workflow action event; use other case events instead |
CASE_COMMENT_ADDED:V1 | New comment entered on a case |
CASE_EXTERNAL_ID_UPDATED:V1 | External reference (e.g., ERP/CRM) changed |
TOUR_CREATED:V1 / TOUR_UPDATED:V1 / TOUR_DELETED:V1 | Tour lifecycle events |
VEHICLE_LOCATION_UPDATED:V1 <strong style={{color:'#c62828'}}>DEPRECATED | Vehicle GPS location was updated |
AREA_WAITING_TIME_UPDATED:V1 | Operational waiting time for an area changes |
HEARTBEAT:V1 | Synthetic heartbeat emitted roughly every 30β―s |
Connection lifecycle, limits, and errorsβ
- Timeouts β All SSE connections are automatically dropped by the server every 90 seconds forcing the client to re-establish the connection. This shorter timeout ensures that dead connections (e.g., those dropped by proxies or load balancers) are quickly detected and cleaned up. The client will automatically reconnect with an up-to-date JWT token.
- Heartbeats β Sent every ~30 seconds. They let you detect stale connections without application data. Ignore them if you only care about business events. If the backend fails to send a heartbeat (indicating a dead connection), the connection is automatically cleaned up.
- Stale connection cleanup β The server periodically checks for connections that haven't successfully sent any data (including heartbeats) and automatically cleans them up. This prevents resource leaks from network-level connection failures.
- Rate limiting β The server enforces both throughput limits (to avoid flooding) and a per-user concurrent connection cap (default 5). Close unused browser tabs or EventSource instances to stay under the limit.
- Replay cache β Only the most recent ~100 events are retained for
lastEventIdcatch-up. If you reconnect after that window, perform a manual resync through the REST APIs.
Error bodies follow ErrorDTO. See the error handling guide.