A FastAPI backend service that generates interactive call flow graphs for NetSapiens PBX systems. It acts as a middleware to crawl call logic (DIDs, Users, Auto Attendants, Queues) and produces a directed graph for visualization.
- BFS Crawler: Deep mapping of call flows from Ingress (DID) to endpoints.
- Resilient Client: Automatic failover across multiple NetSapiens API clusters.
- Security: Strict API URL whitelisting with wildcard support.
- Frontend Integration: Serves a dynamic JavaScript module for easy injection into the NetSapiens portal.
The application generates a hierarchical view of your call logic, making it easy to understand complex routing.

Use the "Phone Number Filter" button to toggle visibility for specific ingress numbers.
Isolating a specific DID allows you to trace its unique path through the system.

The Time Simulator allows you to test how calls route at specific times (e.g., After Hours, Holidays) without waiting.
Weekend Simulation (Route changes to gray/dashed for inactive paths):

Right-click on any Node (User, Auto Attendant, etc.) or Edge (Connection) to view raw API data, configuration parameters, and detailed logic.
- Python 3.11+
pip- Docker (optional, recommended for production)
The application is configured via environment variables. Create a .env file (see .env.example) or set these in your container environment.
| Variable | Description | Example |
|---|---|---|
PUBLIC_API_URL |
Required. The public URL where this API is reachable by the browser. Used to configure the injected JavaScript. | http://localhost:8000/graph |
ALLOWED_DOMAINS_ENV |
(Optional) Comma-separated list of allowed domains. Merged with allowed_domains.json. |
api.netsapiens.com,*.my-pbx.com |
NS_API_TOKEN |
(Development Only) Bearer token for local testing scripts. | None |
NS_DOMAIN |
(Development Only) Domain for local testing scripts. | None |
-
Clone & Install:
git clone <repo_url> python -m venv venv source venv/bin/activate pip install -r requirements.txt
-
Run Server:
uvicorn main:app --reload
The API will be available at
http://localhost:8000.
-
Configure Environment: Ensure your
.envfile has the correctPUBLIC_API_URLpointing to your production domain (e.g.,https://graph.yourdomain.com/graph). -
Start Service:
docker compose up --build -d
docker build -t route-graph .
docker run -d \
-p 8000:8000 \
-e PUBLIC_API_URL="https://graph.yourdomain.com/graph" \
-v $(pwd)/allowed_domains.json:/app/allowed_domains.json \
route-graphThis service is designed to run behind a reverse proxy (Traefik, Nginx, Caddy).
It automatically trusts X-Forwarded-* headers from all IPs to ensure correct protocol detection (HTTP vs HTTPS) for generating links.
To prevent SSRF or abuse, the API checks the api_url parameter against a whitelist. You can configure this list using either a JSON file (hot-reloadable) or an environment variable. The system merges both lists.
- File:
allowed_domains.json - Behavior: Updates take effect immediately without restarting the server.
- Format:
{ "allowed_domains": [ "api.netsapiens.com", "*.trusted-pbx.com" ] }
- Variable:
ALLOWED_DOMAINS_ENV - Format: Comma-separated list (supports wildcards).
- Example:
ALLOWED_DOMAINS_ENV=api.netsapiens.com,*.my-pbx.com
The backend serves a pre-configured JavaScript module that injects the visualizer into the NetSapiens inventory tab.
-
Locate Script URL: Your script is served at:
https://<your-service-domain>/static/route_graph_inventory_tab.js -
Configure NetSapiens: In the NetSapiens portal settings (or via API), add this URL to the
PORTAL_EXTRA_JSconfiguration value.Note: The script is dynamically generated to use the
PUBLIC_API_URLyou defined, ensuring the frontend always knows where to send graph requests. If you already have a javascript injection file, you can add the following to the top of your JS file to load the extra .js file needed:// Load bundleRouter.bundle.js asynchronously (function() { // Create and append the bundleRouter script const bundleRouterScript = document.createElement('script'); bundleRouterScript.src = 'https://<your-service-domain>/static/route_graph_inventory_tab.js'; // Set async to true to load it asynchronously without blocking bundleRouterScript.async = true; // Append to head to ensure it loads as soon as possible document.head.appendChild(bundleRouterScript); })();
-
Set Portal UI configs (v44+): Set
PORTAL_CSP_CONNECT_ADDITIONSwithhttps://<your-service-domain>.comfor the server where the script is located. If you are loading the script inside an existing JS injection, you must add the following forPORTAL_CSP_SCRIPT_ADDITIONS:
https://<your-service-domain> https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.28.1/cytoscape.min.js
OR, if specifying the script directly viaPORTAL_EXTRA_JSjust add
https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.28.1/cytoscape.min.js
If the "Route Graph" tab does not appear or behaves unexpectedly:
- Open Browser Developer Tools (F12) -> Console.
- Enable debug mode:
localStorage.setItem("ROUTE_GRAPH_DEBUG", "true");
- Reload the page. Verbose logs will appear in the console.



