Set up your server
Install the SDK​
Install the SDK for the language of your choice. We provide libraries for Node and Python.
It's also possible to use the bare REST API, in this case you can skip this step.
- npm
- yarn
- pip
- poetry
- uv
npm install @fishjam-cloud/js-server-sdk
yarn add @fishjam-cloud/js-server-sdk
pip install fishjam-server-sdk
poetry add fishjam-server-sdk
uv add fishjam-server-sdk
Setup your client​
Let's setup everything you need to start communicating with a Fishjam instance.
First of all, view your app in the Fishjam developer panel and copy your Fishjam ID and the Management Token.
They are required to proceed. Now, we are ready to dive into the code.
FishjamClient.create constructs the client and pings the Fishjam backend with the supplied credentials, so a bad fishjamId or managementToken fails at startup instead of on the first room operation.
- Typescript
- Python
import {FishjamClient } from '@fishjam-cloud/js-server-sdk'; letfishjamClient = awaitFishjamClient .create ({fishjamId :process .env .FISHJAM_ID !,managementToken :process .env .FISHJAM_MANAGEMENT_TOKEN !, }); // The above is roughly equivalent to:fishjamClient = newFishjamClient ({fishjamId :process .env .FISHJAM_ID !,managementToken :process .env .FISHJAM_MANAGEMENT_TOKEN !, }); awaitfishjamClient .checkCredentials ();
import os from fishjam import FishjamClient fishjam_client = FishjamClient.create_and_verify( fishjam_id=os.environ["FISHJAM_ID"], management_token=os.environ["FISHJAM_MANAGEMENT_TOKEN"], ) # The above is roughly equivalent to: fishjam_client = FishjamClient(...) fishjam_client.check_credentials()
Managing rooms​
Create a room to get the roomId and be able to start adding peers.
- Typescript
- Python
constcreatedRoom = awaitfishjamClient .createRoom (); consttheSameRoom = awaitfishjamClient .getRoom (createdRoom .id ); awaitfishjamClient .deleteRoom (theSameRoom .id ) // puff, it's gone!
created_room = fishjam_client.create_room() the_same_room = fishjam_client.get_room(created_room.id) fishjam_client.delete_room(the_same_room.id) # puff, it's gone!
Managing peers​
Create a peer to obtain the peer token allowing your user to join the room. At any time you can terminate user's access by deleting the peer.
- Typescript
- Python
const {peer ,peerToken } = awaitfishjamClient .createPeer (created_room .id ); awaitfishjamClient .deletePeer (created_room .id ,peer .id );
peer, token = fishjam_client.create_peer(room_id) fishjam_client.delete_peer(room_id, peer.id)
Metadata​
When creating a peer, you can also assign metadata to that peer, which can be read later with the client SDK. This metadata can be only set when creating the peer and can't be updated later.
- Typescript
- Python
const {peer ,peerToken } = awaitfishjamClient .createPeer (created_room .id , {metadata : {realName : 'Tom Reeves' }, });
options = PeerOptions( metadata={"realName": "Tom Reeves"}, ) peer, token = self.fishjam_client.create_peer(room_id, options=options)
Listening to events​
Fishjam instance is a stateful server that is emitting messages upon certain events.
You can listen for those messages and react as you prefer.
There are two options to obtain these.
Webhooks​
Configure your webhook URL in the Webhooks tab of the Fishjam Dashboard. Fishjam then delivers all notifications to that URL.
We recommend also enabling notification batching when creating a room. Fishjam then coalesces several notifications into a single request, delivering them faster and with fewer HTTP requests — which improves your backend's response time under load.
- Typescript
- Python
awaitfishjamClient .createRoom ({batchWebhookNotifications : true });
from fishjam import RoomOptions options = RoomOptions( batch_webhook_notifications=True, ) fishjam_client.create_room(options)
On the receiving side, decode the raw request body with the SDK's decoder, then iterate the result and react to the events you care about. The decoder returns a list of notifications and transparently unwraps a batch — a single notification simply comes back as a one-element list, so the same handler works whether or not batching is enabled.
- Typescript
- Python
for (const {type ,notification } ofdecodeServerNotifications (rawBody )) { switch (type ) { case 'peerConnected':console .log (`Peer ${notification .peerId } joined room ${notification .roomId }`); break; case 'peerDisconnected':console .log (`Peer ${notification .peerId } left room ${notification .roomId }`); break; case 'roomCreated':console .log (`Room ${notification .roomId } created`); break; default: break; } }
from fishjam import decode_server_notifications from fishjam.events import ( ServerMessagePeerConnected, ServerMessagePeerDisconnected, ServerMessageRoomCreated, ) for notification in decode_server_notifications(raw_body): match notification: case ServerMessagePeerConnected(): print(f"Peer {notification.peer_id} joined room {notification.room_id}") case ServerMessagePeerDisconnected(): print(f"Peer {notification.peer_id} left room {notification.room_id}") case ServerMessageRoomCreated(): print(f"Room {notification.room_id} created") case _: ...
Verifying webhook signatures​
Every webhook request is signed so you can confirm it really came from Fishjam. Each delivery carries an x-fishjam-signature-256: sha256=<hex> header — an HMAC-SHA256 of the raw request body, keyed with your webhook secret. Find (and rotate) that secret in the Webhooks tab of the Fishjam Dashboard.
Verify the header against the raw body before decoding, and reject mismatches with 401. Verification needs the exact bytes Fishjam sent, so read the raw body before any parsing.
- Typescript
- Python
import {verifyWebhookSignature ,decodeServerNotifications } from '@fishjam-cloud/js-server-sdk'; constsecret =process .env .FISHJAM_WEBHOOK_SECRET !; // rawBody: Buffer, signatureHeader: req.headers['x-fishjam-signature-256'] if (!verifyWebhookSignature (rawBody ,signatureHeader ,secret )) { // respond with 401 here — how depends on your framework, see the examples linked below throw newError ('Invalid webhook signature'); } // signature is valid — safe to decode constnotifications =decodeServerNotifications (rawBody );
import os from fishjam import verify_webhook_signature, decode_server_notifications secret = os.environ["FISHJAM_WEBHOOK_SECRET"] # raw_body: bytes, signature_header: request.headers["x-fishjam-signature-256"] if not verify_webhook_signature(raw_body, signature_header, secret): # respond with 401 here — how depends on your framework, see the examples linked below raise PermissionError("Invalid webhook signature") # signature is valid — safe to decode notifications = decode_server_notifications(raw_body)
See the Fastify and FastAPI examples for a full webhook handler wired into a web framework.
SDK Notifier​
Our SDKs come equipped with a Notifier allowing you to subscribe for messages. It sets up a websocket connection with a Fishjam instance and provides a simple interface allowing you to handle messages.
- Typescript
- Python
import {FishjamWSNotifier } from '@fishjam-cloud/js-server-sdk'; constonClose =console .log ; constonError =console .error ; constonConnectionFailed =console .error ; constfishjamNotifier = newFishjamWSNotifier ({fishjamId ,managementToken },onError ,onClose );fishjamNotifier .on ('roomCreated',console .log );
import asyncio from fishjam import FishjamNotifier from fishjam.events import ServerMessageRoomCreated notifier = FishjamNotifier(fishjam_id, management_token) @notifier.on_server_notification def handle_notification(notification): match notification: case ServerMessageRoomCreated(): print(notification) case _: ... async def run_notifier(): notifier_task = asyncio.create_task(notifier.connect()) # Wait for notifier to be ready to receive messages await notifier.wait_ready() await notifier_task asyncio.run(run_notifier())