Overview When running multiple tenants on the same Sisense instance (self-contained multitenancy), you may need to migrate data source connections from one tenant to another. The Connection Manager stores connections in MongoDB, and each connection is scoped to a specific tenant. Sisense does not provide a built-in UI-based migration for connections between tenants, so migration must be performed via the REST API or directly at the MongoDB level. This article covers the recommended approaches for migrating connections between tenants on the same instance. Prerequisites Admin or Data Admin role on both the source and target tenants. Access to the Sisense REST API (bearer token with appropriate permissions). For MongoDB-level migration: SSH access to the Sisense server and familiarity with MongoDB commands. Sisense version L2023.1 or later (Connection Manager GA). Approach 1: REST API (Recommended) This is the safest and most maintainable approach. It uses the Sisense v2 Connections API to read connections from one tenant and recreate them on another. Step 1 - List connections on the source tenant Generate an API token for a user on the source tenant and retrieve all connections: curl -X GET "https://<your-instance>/api/v2/connections" \
-H "Authorization: Bearer <SOURCE_TENANT_TOKEN>" \
-H "Content-Type: application/json" This returns a JSON array of all connections visible to the authenticated user on that tenant, including their oid , name , provider , and encrypted parameters . Step 2 - Decrypt connection parameters (if needed) Connection parameters (including passwords) are stored encrypted. To retrieve the plaintext values for recreation on the target tenant, use the decryption endpoint: curl -X GET "https://<your-instance>/api/v1/encryption/decrypt?value=<ENCRYPTED_PARAMETERS>" \
-H "Authorization: Bearer <SOURCE_TENANT_TOKEN>" Note: The first decryption call returns all parameters except the password (still encrypted). Run a second decryption call with just the encrypted password value to fully decrypt it. Step 3 - Create connections on the target tenant Generate an API token for a user on the target tenant and create each connection: curl -X POST "https://<your-instance>/api/v2/connections" \
-H "Authorization: Bearer <TARGET_TENANT_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"name": "My Connection Name",
"provider": "PostgreSQL",
"parameters": {
"Server": "<server-address>",
"Database": "<database-name>",
"UserName": "<username>",
"Password": "<password>"
},
"schema": "public",
"timeout": 300
}' Repeat for each connection you need to migrate. Step 4 - Update data models to use the new connections After importing data models (via .smodel export/import or API), update each model's datasource to point to the newly created connection on the target tenant: curl -X PATCH "https://<your-instance>/api/v2/datamodels/<MODEL_ID>/datasources/<DATASOURCE_ID>" \
-H "Authorization: Bearer <TARGET_TENANT_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"connection": "<NEW_CONNECTION_OID>"
}' Approach 2: MongoDB collection-level migration For bulk migrations or when API access is impractical, you can export and import the connections collection directly in MongoDB. Step 1 - Access the MongoDB container # Single-node deployment
kubectl -n sisense exec sisense-mongod-0 -c mongod-container -it -- bash
# Multi-node deployment
kubectl -n sisense exec sisense-mongodb-replicaset-0 -c mongod-container -it -- bash Step 2 - Export connections from the source tenant mongoexport --db prismWebDB --collection connections \
--query '{"tenantId": "<SOURCE_TENANT_ID>"}' \
--out /tmp/connections_export.json Step 3 - Modify the exported data Before importing into the target tenant, update the following fields: tenantId - Replace the source tenant ID with the target tenant ID. _id - Remove so MongoDB generates new unique IDs. oid - Remove or regenerate to prevent collisions. cat /tmp/connections_export.json | jq 'del(._id) | del(.oid) | .tenantId = "<TARGET_TENANT_ID>"' > /tmp/connections_import.json Step 4 - Import connections into the target tenant mongoimport --db prismWebDB --collection connections \
/tmp/connections_import.json Step 5 - Restart services kubectl -n sisense rollout restart deployment sisense-management Approach 3: Data model export/import with connection re-mapping This approach is best when migrating complete data models along with their connections. Overview of the workflow Export the data model from the source tenant. Import the data model into the target tenant. Update connection references on the target tenant. Build (ElastiCube) or publish (Live model). Optionally, migrate associated dashboards. Step 1 - Export the data model from the source tenant Option A: Via the UI Log into the source tenant. Navigate to Data page. Click three-dot menu > Export Model. The .smodel file downloads. Option B: Via the REST API GET {{URL}}/api/v2/datamodel-exports/stream/schema?datamodelId=<MODEL_ID>
Authorization: Bearer <SOURCE_TENANT_TOKEN> Option C: Via Sisense CLI si elasticubes export -name "Model Name"
# Output: /opt/sisense/storage/backups/ Step 2 - Import the data model into the target tenant Option A: Via the UI Log into the target tenant. Navigate to Data page. Click Import Model. Select the .smodel file. Option B: Via the REST API POST {{URL}}/api/v2/datamodel-imports/stream/full
Authorization: Bearer <TARGET_TENANT_TOKEN>
Content-Type: multipart/form-data
# Attach the .smodel file Option C: Via Sisense CLI si elasticubes import -path "/opt/sisense/storage/backups/Model.sdata" -start false Step 3 - Update connection settings on the target tenant Method A: UI (Change Connection) Open the imported model on the Data page. Click on a datasource table. Click three-dot menu > Connection Settings > Change Connection. Select existing connection or create new one with target parameters. Click Test Connection to verify. Click Next, verify table mapping. Repeat for each datasource. Method B: REST API (post-import patching) Step 3B.1 - Get the model schema: GET {{URL}}/api/v2/datamodels/<DATAMODEL_ID>/schema
Authorization: Bearer <TARGET_TENANT_TOKEN> Step 3B.2 - Create a connection on target: POST {{URL}}/api/v2/connections
Authorization: Bearer <TARGET_TENANT_TOKEN>
Content-Type: application/json
{
"name": "Production PostgreSQL",
"provider": "PostgreSQL",
"parameters": {
"Server": "prod-db.example.com",
"Port": "5432",
"Database": "analytics",
"UserName": "sisense_user",
"Password": "your_password"
},
"schema": "public",
"timeout": 300
} Step 3B.3 - Update each dataset: PATCH {{URL}}/api/v2/datamodels/<DATAMODEL_ID>/schema/datasets/<DATASET_ID>
Authorization: Bearer <TARGET_TENANT_TOKEN>
Content-Type: application/json
{
"connection": "<NEW_CONNECTION_OID>"
} Method C: Pre-import .smodel modification Before importing, modify the .smodel JSON file. Original (encrypted): "connection": {
"id": "507f1f77bcf86cd799439011",
"provider": "PostgreSQL",
"parameters": "qkSH7Ktg...(encrypted)...",
"schema": "public",
"timeout": 300,
"protectedParameters": ["Password"]
} Modified (plaintext): "connection": {
"id": "",
"provider": "PostgreSQL",
"Parameters": {
"Server": "prod-db.example.com",
"Port": "5432",
"Database": "analytics",
"UserName": "sisense_user",
"Password": "your_password"
},
"schema": "public",
"timeout": 300,
"protectedParameters": []
} Key changes: empty id, uppercase Parameters with plaintext, empty protectedParameters. Step 4 - Build or publish the model For ElastiCubes: POST {{URL}}/api/v2/builds
Authorization: Bearer <TARGET_TENANT_TOKEN>
{"datamodelId": "<ID>", "buildType": "full", "rowLimit": 0} For Live models: POST {{URL}}/api/v2/builds
Authorization: Bearer <TARGET_TENANT_TOKEN>
{"datamodelId": "<ID>", "buildType": "publish", "rowLimit": 0} Step 5 - Migrate associated dashboards (optional) # Export
GET {{URL}}/api/v1/dashboards/<DASHBOARD_ID>/export/dash
# Import
POST {{URL}}/api/v1/dashboards/import End-to-End Example: Migrating "Sales Analytics" from dev to prod tenant Environment: Sisense instance at https://bi.acme.com. Source tenant: acme-dev. Target tenant: acme-prod. Model: Sales Analytics (PostgreSQL, 3 tables). Example: Method A (UI) Export from acme-dev: Data page > Sales Analytics > Export Model > downloads .smodel file. Import to acme-prod: Data page > Import Model > select file. Open model - see red connection errors on orders, customers, products tables. Click orders table > Connection Settings > Change Connection. Enter: Server=prod-db.internal.acme.com, User=sisense_etl, Password=Pr0d_S3cur3_P@ss!, Database=sales_dwh. Click Test Connection - success. Click Next - verify tables. Apply to all tables from same source - Yes. Click Build > Full Build. Build completes in ~4 minutes. Done. Example: Method B (REST API) Step 1 - Get model ID: curl -s -X GET "https://bi.acme.com/api/v2/datamodels/schema" \
-H "Authorization: Bearer <DEV_TOKEN>" | jq '.[] | select(.title == "Sales Analytics") | .oid'
# Returns: "64b2c3d4e5f6a7b8c9d0e1f2" Step 2 - Export model: curl -s -X GET "https://bi.acme.com/api/v2/datamodel-exports/stream/schema?datamodelId=64b2c3d4e5f6a7b8c9d0e1f2" \
-H "Authorization: Bearer <DEV_TOKEN>" -o "Sales_Analytics.smodel" Step 3 - Import to prod: curl -s -X POST "https://bi.acme.com/api/v2/datamodel-imports/stream/full" \
-H "Authorization: Bearer <PROD_TOKEN>" -F "file=@Sales_Analytics.smodel"
# Returns: {"oid": "65c3d4e5f6a7b8c9d0e1f2a3", "title": "Sales Analytics"} Step 4 - Create prod connection: curl -s -X POST "https://bi.acme.com/api/v2/connections" \
-H "Authorization: Bearer <PROD_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"name":"Prod PostgreSQL - Sales","provider":"PostgreSQL","parameters":{"Server":"prod-db.internal.acme.com","Port":"5432","Database":"sales_dwh","UserName":"sisense_etl","Password":"Pr0d_S3cur3_P@ss!"},"schema":"public","timeout":300}'
# Returns: {"oid": "conn_new_prod_pg_67890", ...} Step 5 - Update all datasets: for DS in ds_001_orders ds_002_customers ds_003_products; do
curl -s -X PATCH "https://bi.acme.com/api/v2/datamodels/65c3d4e5f6a7b8c9d0e1f2a3/schema/datasets/$DS" \
-H "Authorization: Bearer <PROD_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"connection":"conn_new_prod_pg_67890"}'
done Step 6 - Build: curl -s -X POST "https://bi.acme.com/api/v2/builds" \
-H "Authorization: Bearer <PROD_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"datamodelId":"65c3d4e5f6a7b8c9d0e1f2a3","buildType":"full","rowLimit":0}'
# Returns: {"status": "building"} Step 7 - Verify build: curl -s -X GET "https://bi.acme.com/api/v2/builds?datamodelId=65c3d4e5f6a7b8c9d0e1f2a3&sort=-startTime&limit=1" \
-H "Authorization: Bearer <PROD_TOKEN>"
# Returns: [{"status": "done", "rowCount": 1458293}] Example: Method C (Pre-import modification) Step 1 - Export from dev (same as Method B Step 2). Step 2 - Run transformation script: import json
with open("Sales_Analytics.smodel", "r") as f:
model = json.load(f)
PROD_CONN = {"Server":"prod-db.internal.acme.com","Port":"5432",
"Database":"sales_dwh","UserName":"sisense_etl",
"Password":"Pr0d_S3cur3_P@ss!"}
for ds in model.get("datasets", []):
if "connection" in ds:
ds["connection"] = {
"id": "",
"provider": ds["connection"]["provider"],
"Parameters": PROD_CONN,
"schema": ds["connection"].get("schema","public"),
"timeout": 300,
"protectedParameters": [],
"uiParams": {},
"globalTableConfigOptions": {}
}
with open("Sales_Analytics_prod.smodel", "w") as f:
json.dump(model, f, indent=2) Step 3 - Import modified file: curl -s -X POST "https://bi.acme.com/api/v2/datamodel-imports/stream/full" \
-H "Authorization: Bearer <PROD_TOKEN>" -F "file=@Sales_Analytics_prod.smodel"
# Connection auto-created, model imported with correct prod settings Step 4 - Build: curl -s -X POST "https://bi.acme.com/api/v2/builds" \
-H "Authorization: Bearer <PROD_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"datamodelId":"65c3d4e5f6a7b8c9d0e1f2a3","buildType":"full","rowLimit":0}' Step 5 - Delete the modified file (contains plaintext password): rm Sales_Analytics_prod.smodel Important Considerations Connections are scoped per tenant - Tenant A connections are not visible from Tenant B. Starting with L2025.1, Connection Manager supports shared/deduplicated connections. Passwords are encrypted with instance-level keys - valid when moving between tenants on same server. OAuth connections (BigQuery, Salesforce) may require re-authentication on target tenant. SSH tunnel configs are instance-level and shared across tenants. Use GET /api/v2/connections/{id}/getAllDependencies to check before deleting source connections. Verification Log into target tenant > Data > Connection Manager to confirm connections appear. Test each connection or run a build. Verify dashboards load data correctly. Troubleshooting Connection not visible: tenantId not updated in MongoDB migration. Cannot establish connection: credentials not properly handled - recreate via UI. Duplicate names: rename before/after import. Models pointing to old connections: use Change Connection or PATCH via API. Related Resources Sisense REST API - Connections: https://sisense.dev/guides/restApi/ Exporting and Importing Data Models: https://docs.sisense.com/win/SisenseWin/exporting-and-importing-models.htm Data Source Connection Management: https://docs.sisense.com/main/SisenseLinux/data-source-connection-management.htm Sisense Multitenancy Overview: https://docs.sisense.com/main/SisenseLinux/sisense-multitenancy.htm