Octopus
Octopus Developersgateway.octopusoperations.co.za/v1

Guides

Realtime changes

A Socket.IO namespace at /v1. Connect once with the same key and Octopus pushes a compact change event whenever something you can see moves, whoever moved it.

A session, start to finish

Connect, describe, subscribe
Drawing…

Step 3 is the mechanism worth understanding. On connect you are placed in one room per .read scope you hold, and change events are published to those rooms, so you receive exactly the resources you are allowed to see and nothing else. The filtering is structural, not a check applied per message.

Steps 4 and 5 answer the same question twice, deliberately. ready is pushed the instant you connect, which suits a client that registers handlers before connecting. A client that awaits connection first and subscribes afterwards would miss that packet entirely, nothing replays it, so describe asks for the same answer at any time.

Do not depend on ready if you subscribe late

This exact race cost the test suite three assertions before it could cost an integrator an afternoon. If your client connects and then attaches handlers, call describe instead.

Connecting

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

const socket = io("https://gateway.octopusoperations.co.za/v1", {
  transports: ["websocket"],
  auth: { apiKey: process.env.OCTOPUS_API_KEY },
});

socket.on("ready", (d) => console.log("subscribed to", d.subscribedTo));

socket.on("change", (c) => {
  if (c.resource === "tasks") refreshTask(c);
});

// Request/response works too: <event> / <event>Success / <event>Error
socket.emit("listTasks", {});
socket.on("listTasksSuccess", (r) => console.log(r.populatedItem.length));

This namespace is for applications only

A signed-in person is refused here on purpose. The guarantees of /v1 are written in terms of scopes, and a person holds roles instead. Letting one in would mean a different rule set than the contract describes.

What a change event carries

Deliberately small: enough to know what moved and to fetch it if you care, never a full record you did not ask for.

json
{
  "v": 1,
  "id": "b754d5dc-f1a5-45cc-9220-7571e992dd1f",
  "type": "TASK_UPDATED",
  "resource": "tasks",
  "action": "updated",
  "orgId": "4bca402b-e846-4bde-8489-ee894cd80d1c"
}
  • v: the change schema version, currently 1.
  • id: unique per change. Use it to deduplicate.
  • resource: the family. The easiest thing to switch on.
  • action: one of created, updated or deleted.

Which scope hears which changes

ResourceEvent prefixScope needed
tasksTASK_tasks.read
projectsPROJECT_projects.read
risksRISK_projects.read
issuesISSUE_projects.read
calendarCALENDAR_calendar.read
clientsCLIENT_clients.read
documentsDOCUMENT_documents.read
financialsFINANCIAL_financials.read
assetsASSET_assets.read
personnelPERSONNEL_personnel.read
certificatesCERTIFICATE_personnel.read
complianceCOMPLIANCE_personnel.read
organisationORGANISATION_organisation.read