Skip to main content
Version: Next

How to Deploy Fishjam to Production

How-to Guide - Deploy your Fishjam backend safely to production

This guide covers the essential steps to move from development (using the Sandbox API) to a production-ready Fishjam backend deployment.

Prerequisites

  • Working Fishjam backend (see Backend Quick Start)
  • A Fishjam account on the Standard Jar plan (recommended for production deployments)
  • Domain and SSL certificates for your backend
  • Production database and infrastructure

Step 1: Get your Fishjam credentials

Get credentials from the dashboard

  1. Log in to Fishjam Dashboard
  2. Copy your Fishjam ID and Management Token from the dashboard
tip

The Standard Jar plan is recommended for production deployments. With the Mini Jar plan, you can still develop and test using the Sandbox API, but production deployments should use the Standard Jar plan to get full access and reliability guarantees.

Environment variables setup

Create your environment variables:

# Fishjam credentials FISHJAM_ID="your-fishjam-id" FISHJAM_MANAGEMENT_TOKEN="your-management-token" # Your application settings NODE_ENV="production" PORT="3000" DATABASE_URL="your-production-database-url" # Security settings JWT_SECRET="your-secure-jwt-secret" CORS_ORIGIN="https://yourdomain.com"

Step 2: Replace Sandbox API with proper authentication

Replace Sandbox API calls

Remove any Sandbox API dependencies from your client code:

// ❌ Remove: Sandbox API calls const peerToken = await getSandboxPeerToken(roomName, userName); // ✅ Replace with: Your authenticated API const response = await fetch("https://your-backend.com/api/join-room", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${userToken}`, }, body: JSON.stringify({ roomName, userName }), });

Implement user authentication

const fishjamClient = new FishjamClient({ fishjamId: process.env.FISHJAM_ID!, managementToken: process.env.FISHJAM_MANAGEMENT_TOKEN!, }); // Authentication middleware const authenticateUser = async ( req: express.Request, res: express.Response, next: express.NextFunction, ) => { try { const token = req.headers.authorization?.replace("Bearer ", ""); if (!token) { return res.status(401).json({ error: "Authentication required" }); } const decoded = jwt.verify(token, process.env.JWT_SECRET); req.user = await getUserById(decoded.userId); next(); } catch (error) { res.status(401).json({ error: "Invalid token" }); } }; // Protected endpoint for joining rooms app.post("/api/join-room", authenticateUser, async (req, res) => { try { const { roomName } = req.body; const user = req.user; // Create or get room const room = await getOrCreateRoom(roomName); // Verify user has permission to join this room if (!canUserJoinRoom(user, room)) { return res.status(403).json({ error: "Access denied" }); } // Create peer with user metadata const { peer, peerToken } = await fishjamClient.createPeer(room.id, { metadata: { userId: user.id, name: user.name, avatar: user.avatar, role: user.role, }, }); res.json({ peerToken }); } catch (error) { console.error("Join room error:", error); res.status(500).json({ error: "Failed to join room" }); } });

(Optional) Step 3: Handle webhooks and events

You may wish to receive events from Fishjam regarding created rooms and peers. All you need for this is a single api endpoint:

Webhook endpoint

// Webhook signature verification const verifyWebhookSignature = ( req: express.Request, res: express.Response, next: express.NextFunction, ) => { const signature = req.headers["x-fishjam-signature"]; const payload = JSON.stringify(req.body); const expectedSignature = crypto .createHmac("sha256", process.env.WEBHOOK_SECRET) .update(payload, "utf8") .digest("hex"); if (signature !== expectedSignature) { return res.status(401).json({ error: "Invalid signature" }); } next(); }; // Webhook handler app.post( "/api/webhooks/fishjam", express.raw({ type: "application/json" }), verifyWebhookSignature, (req: express.Request, res: express.Response) => { const event = req.body; switch (event.type) { case "peer_connected": handlePeerConnected(event.data); break; case "peer_disconnected": handlePeerDisconnected(event.data); break; case "room_empty": handleRoomEmpty(event.data); break; default: console.log("Unhandled event type:", event.type); } res.status(200).json({ received: true }); }, );

Enabling webhooks

Now, with your endpoint setup, all you need to do is supply your webhook endpoint to Fishjam when creating a room:

const createRoomWithWebhooks = async ( roomName: string, roomType = "conference", ) => { const room = await fishjamClient.createRoom({ roomType, webhookUrl: `${process.env.BASE_URL}/api/webhooks/fishjam`, }); return room; };

Common production issues

Issue: Token expiration handling

Peer tokens expire 24h after creation. We encourage keeping room and peer lifetimes as short as possible (typically a single room corresponds to a single video call or stream). However, if you wish to reuse a single peer over multiple days, you can make use of token refreshing:

const newToken = fishjamClient.refreshPeerToken(roomId, peerId);

See also

For scaling considerations:

For specific backend frameworks: