WebSockets
Subspace is a read-only API that allows your system to recieve streaming real-time information about changes to your Appointments, Warehouses, Docks, etc.
Using Subspace you can implement a "push"-based approach to your integration instead of relying only on "poll"-ing methods (which can be inefficient).
Subpsace is based on the famous socket.io
library.
Choosing a socket.io Client
The socket.io project provides a JavaScript client library that works in the browser as well as NodeJS. However there are also client implementations in many other languages including C#, Java, Python, and Go, so you should select the appropriate client for your project. A good overview of socket.io and a list of client implementations can be found here: Socket.IO Introduction
Currently Opendock uses socket.io server v4.x so please make sure to select an appropriate socket.io client version that is protocol compatible.
Connecting and Authentication
The base connection URL is the same as the base URL for Neutron above, except with the word "subspace" instead of " neutron". For example, our production Subspace connection URL is wss://subspace.opendock.com.
For convenience, the same JWT token obtained from the Neutron /auth/login
endpoint above is used for Subspace
authentication.
Connecting and Authenticating are done in a single operation: simply connect to the following wss
URL:
<Connection URL>?token=<JWT Token>
That can be a little confusing to parse. Here's a real-life example connection string:
wss://subspace.opendock.com?token=eyJhbGciOiJIUzI1Ni...(full token continues)
Currently Opendock only supports the websocket
transport, so you must specify this in your connection
settings.
Here's an example of connecting to Subspace using the JavaScript client:
// NOTE: we assume "accessToken" was already obtained earlier via a call to '/auth/login'.
const baseSubspaceUrl = 'wss://subspace.opendock.com';
const url = `${baseSubspaceUrl}?token=${accessToken}`;
const socket = io(url, { transports: ['websocket'] }); // Enforce 'websocket' transport only.
Listening to events
Subspace emits Create/Update/Delete events for each entity in your Org (Appointment, Warehouse, Dock, etc). Your event handler for these events will receive a JSON object containing the details about the given entity.
Once your socket.io client instance is connected, you can listen for any of these events by constructing the appropriate event string:
Event strings follow this pattern:
"{EventType}-{EntityName}"
EventType
can be one of: create
, update
, or delete
.
EntityName
can be any entity in our REST API, such as: Appointment
, Warehouse
, Dock
, etc.
So for example, to listen to create
events for Appointment
entities you would use:
"create-Appointment"
Or to listen to update
events for Warehouse
entities you would use:
"update-Warehouse"
NOTE: The event types are lowercase, but the entity names are capitalized (event strings are case-sensitive).
There is also a "heartbeat"
event that you can listen to, which will emit every 5 seconds with a timestamp and the
Neutron API version. This can be helpful for ensuring that your connection to Subspace is working correctly.
Caveats and Limitations
Subspace does not do any sort of "catch-up" or "replay" of events, you will only get the events that occur after you connect to the socket.io server.
If your client loses connection for some time, the event messages will not be queued and delivered when you next connect, you will simply start receiving new messages after the point in time that you connected.
For this reason, even when using Subspace, you may need to occasionally supplement with calls to our REST API (ie. getAll) to fetch entities and keep in sync with the data in Opendock, depending on your needs.
Examples
Listening for Heartbeat
This event handler will get called periodically with the "heartbeat" information:
socket.on('heartbeat', (data) => {
console.log(data);
});
This will output something like:
{
now: '2022-09-15T20:02:20.015Z',
version: {
major: '2',
minor: '5',
patch: '16',
commit: '4a443fb\n'
}
}
Listening for Appointment Creation and Update
In this example, your event handler will get called whenever an Appointment is created in your Org:
socket.on('create-Appointment', (data) => {
console.log('appt create:', data);
});
The data
your event handler recieves will be a JSON object containing
the Appointment details, like this:
{
"id": "9cd63603-a7ff-43c7-8183-befc19a7a81b",
"createDateTime": "2022-07-29T06:49:20Z",
"lastChangedDateTime": "2022-07-29T06:49:20Z",
"isActive": true,
"tags": [],
"type": "Standard",
"status": "Scheduled",
"start": "2022-07-29T00:00:00+00:00",
"end": "2022-07-29T01:30:00+00:00",
...
...
...
}
If you also wanted to listen for any changes to existing Appointments you could add another listener:
socket.on('create-Appointment', (data) => {
console.log('appt create:', data);
});
socket.on('update-Appointment', (data) => {
console.log('appt update:', data);
});
The update-Appointment
event handler will receive a similar JSON object
containing the most up-to-date details of the Appointment that was
just updated.