SSE Notification
The SSE (Server-Sent Events) notification stream is the primary mechanism for waking up a service client when new orders or commands are available. The stream sends lightweight notifications only. It never contains full order or command payloads. When a notification is received, the client should immediately poll GET /orders/location for the actual data.
Endpoint
GET https://{host}/notifications/service/streamHeaders:
| Header | Required | Description |
|---|---|---|
api_key | Yes | Service device API key |
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
orderNumber | long | -1 | Last processed order number for offset-based reconnect |
commandNumber | long | 0 | Last processed command ID for offset-based reconnect |
Response: text/event-stream (SSE stream using SseEmitter)
Event Flow
Once connected, the server sends events in the following sequence:
Client Server
| |
|--- GET /notifications/service/stream --->|
| |
|<-- event: ready |
| data: {"subscriptionId": "...",|
| "locationName": "..."} |
| |
|<-- event: message | (when new orders or commands)
| data: {"apiKey": "...", |
| "licenseCode": "...", |
| "scope": "..."} |
| |
|<-- event: keepalive | (every 15 seconds)
| data: {} |1. Ready Event
On successful connection, the server immediately sends a ready event with the subscription metadata:
event: ready
data: {"subscriptionId":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","locationName":"Main Floor"}The subscriptionId uniquely identifies this SSE connection. The locationName indicates which location the client is subscribed to.
2. Message Events
When new orders or commands are available for the location, the server sends a message event with a MessagesAvailableEvent payload:
event: message
data: {"apiKey":"abc123-def456","licenseCode":"LICENSE01","scope":"SESSION"}The MessagesAvailableEvent payload:
| Field | Type | Description |
|---|---|---|
apiKey | string | The API key of the subscription. |
licenseCode | string | The license code for the tenant. |
scope | string | Either SESSION or LICENSE, indicating the broadcast scope. |
This is a signal to poll. It does not contain any order or command data.
3. Keepalive Events
The server sends a keepalive event every 15 seconds to keep the connection alive:
event: keepalive
data: {}If no keepalive is received for 60 seconds, the client should assume the connection is dead and reconnect.
Connection Timeout
The SSE stream has a timeout of 60 minutes of inactivity. If no data is sent for 60 minutes, the server closes the connection. The keepalive event resets this timeout, so in practice the connection stays open as long as the client receives keepalives.
Reconnection with Offset
When reconnecting after a disconnection, pass the last processed order number and command ID as query parameters. This ensures the server resumes notification from where you left off, and you do not miss any events:
curl -i -H "api_key: abc123-def456-ghi789" \
"https://{host}/notifications/service/stream?orderNumber=12345&commandNumber=42"orderNumber— The highest ordernumberyou have already processed. Pass-1or omit for no offset (start from the beginning).commandNumber— The highest command ID you have already processed. Pass0or omit for no offset.
The server uses these offsets to filter which orders and commands trigger notifications. It tracks the pushed offsets in memory for each subscription.
Error Responses
The SSE stream may return the following HTTP status codes:
| Status | Meaning |
|---|---|
| 401 | Unauthorized — Invalid or missing API key |
| 503 | Service Unavailable — Server is temporarily unavailable. Client should retry with exponential backoff |
Client Implementation Guidance
Using EventSource API (Browser)
const apiKey = "abc123-def456-ghi789";
const lastOrderNumber = 12345; // your last processed order number
const lastCommandNumber = 42; // your last processed command ID
const eventSource = new EventSource(
`https://{host}/notifications/service/stream` +
`?api_key=${apiKey}` +
`&orderNumber=${lastOrderNumber}` +
`&commandNumber=${lastCommandNumber}`
);
eventSource.addEventListener("ready", (event) => {
const data = JSON.parse(event.data);
console.log("Connected. Subscription:", data.subscriptionId);
console.log("Location:", data.locationName);
});
eventSource.addEventListener("message", (event) => {
const data = JSON.parse(event.data);
console.log("New data available for apiKey:", data.apiKey);
// Immediately poll GET /orders/location
pollOrdersAndCommands();
});
eventSource.addEventListener("keepalive", () => {
// Connection is still alive. No action needed.
});
eventSource.onerror = (err) => {
console.error("SSE connection error, reconnecting...", err);
// EventSource automatically reconnects, but you may want to
// implement exponential backoff and update offset params
};Using an SSE Client Library (Java/Kotlin)
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import java.util.concurrent.atomic.AtomicLong
class SseClient(private val apiKey: String) {
private val lastOrderNumber = AtomicLong(-1L)
private val lastCommandNumber = AtomicLong(0L)
private val client = HttpClient.newHttpClient()
fun connect() {
val url = "https://{host}/notifications/service/stream" +
"?api_key=$apiKey" +
"&orderNumber=${lastOrderNumber.get()}" +
"&commandNumber=${lastCommandNumber.get()}"
val request = HttpRequest.newBuilder()
.uri(URI.create(url))
.GET()
.build()
client.send(request, HttpResponse.BodyHandlers.ofLines()).use { response ->
response.body().forEach { line ->
when {
line.startsWith("event: ready") -> handleReady()
line.startsWith("event: message") -> handleMessage()
line.startsWith("data:") -> handleData(line.removePrefix("data:").trim())
}
}
}
}
}Reconnection with Exponential Backoff
function connectWithBackoff(apiKey, maxRetries = 10) {
let retryCount = 0;
let lastOrderNumber = 0;
let lastCommandNumber = 0;
function connect() {
const url = `https://{host}/notifications/service/stream` +
`?api_key=${apiKey}` +
`&orderNumber=${lastOrderNumber}` +
`&commandNumber=${lastCommandNumber}`;
const es = new EventSource(url);
es.addEventListener("ready", (event) => {
retryCount = 0;
lastOrderNumber = 0;
lastCommandNumber = 0;
});
es.addEventListener("message", () => {
pollOrdersAndCommands();
});
es.onerror = () => {
es.close();
if (retryCount < maxRetries) {
const delay = Math.min(1000 * Math.pow(2, retryCount), 60000);
retryCount++;
setTimeout(connect, delay);
}
};
}
connect();
}What the Client Must Do
- Open the SSE connection with the API key.
- On
ready, record thesubscriptionIdandlocationName. - On
message, immediately callGET /orders/location. - Keep track of the last processed
orderNumberandcommandNumberfor reconnection. - If the connection drops, reconnect with exponential backoff, passing the offset parameters.
What the SSE Stream Does NOT Do
- The SSE stream does not contain order data or command payloads. It is a wake-up signal only.
- The SSE stream does not acknowledge that you have processed orders. Processing acknowledgement happens through the status update and command response endpoints.
Next Steps
- Polling — Poll for orders and commands when notified
- Respond to Commands — Respond to commands retrieved via polling