When to use this path
- Your platform already runs WebSocket infrastructure
- You want full visibility into the control plane (prompts, images, session events)
- You want white-labeled endpoints — end users never see Decart
Characteristics
Media quality is identical to using Decart directly. Your WS proxy only handles signaling — video and audio flow peer-to-peer.
Reference implementation
ws-signaling-proxy
A complete working proxy in ~200 lines of TypeScript — message buffering, close-code sanitization, structured logging, and an e2e test.
Architecture
How it works
1
End user connects to your WebSocket endpoint
The end user connects to your platform’s WS URL — they never see a Decart endpoint.
2
Your proxy opens a connection to Decart
Authenticate with Decart using your platform API key and forward the model selection.
Your proxy connects to Decart with your API key.
3
Forward messages bidirectionally
Pump JSON messages between the client and Decart. Every message has a
type field. You can log, validate, or modify messages as they pass through — see the message reference for the full protocol.4
WebRTC connects directly
When the end user’s browser receives the SDP answer and ICE candidates through your proxy, it establishes a direct WebRTC connection to Decart via the IP:port in the ICE candidates. Your proxy is not involved in media.
5
Handle disconnects gracefully
When either side disconnects, close the other connection. Your proxy should handle both directions:
Message reference
Your proxy forwards JSON messages between the client and Decart. Every message has atype field that identifies it.
- Client → Decart
- Decart → Client
Messages your proxy receives from the client and forwards to Decart.
The
Forward as-is. The candidate format matches
offer — SDP offer to establish WebRTC
offer — SDP offer to establish WebRTC
sdp field is a string containing the full SDP (same format as RTCSessionDescription.sdp). Forward as-is.ice-candidate — ICE candidate for connectivity
ice-candidate — ICE candidate for connectivity
RTCIceCandidate.toJSON() — usernameFragment is optional and may be present depending on the browser. Signal end-of-candidates by sending null:prompt — Text prompt for the model
prompt — Text prompt for the model
You can validate, filter, or replace this message before forwarding.
set_image — Reference image with optional prompt
set_image — Reference image with optional prompt
You can validate or reject this message before forwarding.
Moderation
Prompts and images are moderated server-side before being applied to the model. Your proxy sees the result as acknowledgment messages:- Prompt accepted →
prompt_ackwithsuccess: true - Prompt rejected →
prompt_ackwithsuccess: falseand anerrorstring - Image accepted →
set_image_ackwithsuccess: true - Image rejected →
set_image_ackwithsuccess: falseand anerrorstring
Usage tracking
Track usage by watching the generation lifecycle messages:Proxy example
A complete WebSocket proxy using Python andwebsockets. This example authenticates the client, connects upstream, pumps messages with logging, and handles disconnects:
This proxy is stateless — it holds no session state between connections. Each WebSocket connection is an independent pass-through. You can scale horizontally by running multiple proxy instances behind a load balancer.
Client implementation
Your client connects to the proxy over WebSocket and handles WebRTC with standard browser APIs.Message types and fields match the message reference above. In production, add reconnection logic for WebSocket drops and monitor
pc.connectionState for WebRTC failures.Next steps
HTTP Signaling
Same architecture with stateless HTTP instead of WS proxy
Authentication
API key management and client tokens