Tenant Management

Create, list, retrieve, and update tenants in the mindzieStudio platform.

IMPORTANT: All endpoints on this page require a Global API Key. Tenant-specific API keys will receive a 401 Unauthorized error.


List All Tenants

GET /api/tenant

Retrieves a paginated list of all tenants in the system with summary statistics.

Query Parameters

Parameter Type Default Description
page integer 1 Page number for pagination
pageSize integer 50 Number of items per page (max: 100)

Response (200 OK)

{
  "tenants": [
    {
      "tenantId": "12345678-1234-1234-1234-123456789012",
      "name": "acme-corp",
      "displayName": "Acme Corporation",
      "description": "Main tenant for Acme Corporation",
      "caseCount": 50000,
      "maxUserCount": 100,
      "maxAnalystCount": 20,
      "analystCount": 12,
      "userCount": 45,
      "preRelease": false,
      "isAcademic": false,
      "autoload": true,
      "dateCreated": "2024-01-15T10:30:00Z",
      "isDisabled": false
    }
  ],
  "totalCount": 5,
  "page": 1,
  "pageSize": 50
}

Tenant Object Fields

Field Type Description
tenantId GUID Unique identifier for the tenant
name string Unique system name (used in URLs)
displayName string Human-readable display name
description string Description of the tenant
caseCount integer Total number of cases across all datasets
maxUserCount integer Maximum allowed users
maxAnalystCount integer Maximum allowed analysts
analystCount integer Current number of analysts
userCount integer Current number of users
preRelease boolean Whether tenant has pre-release features
isAcademic boolean Whether this is an academic tenant
autoload boolean Whether to auto-load projects
dateCreated datetime When the tenant was created
isDisabled boolean Whether the tenant is disabled

Error Responses

Unauthorized (401):

{
  "error": "This endpoint requires a Global API key. Tenant-specific API keys cannot list all tenants.",
  "hint": "Global API keys can be created at /admin/global-api-keys"
}

Get Tenant by ID

GET /api/tenant/{tenantId}

Retrieves detailed information for a specific tenant by its ID.

Path Parameters

Parameter Type Required Description
tenantId GUID Yes The unique identifier of the tenant

Response (200 OK)

{
  "tenantId": "12345678-1234-1234-1234-123456789012",
  "name": "acme-corp",
  "displayName": "Acme Corporation",
  "description": "Main tenant for Acme Corporation",
  "isAcademic": false,
  "preRelease": false,
  "maxUserCount": 100,
  "maxAnalystCount": 20,
  "maxCases": 100000,
  "dateCreated": "2024-01-15T10:30:00Z",
  "isDisabled": false
}

Error Responses

Not Found (404):

{
  "error": "Tenant with ID '12345678-1234-1234-1234-123456789012' not found"
}

Create Tenant

POST /api/tenant

Creates a new tenant in the system with all necessary infrastructure.

Request Body

{
  "name": "new-tenant",
  "displayName": "New Tenant Corp",
  "description": "Description of the new tenant",
  "maxUsers": 50,
  "maxAnalyst": 10,
  "maxCases": 100000,
  "timeZone": "America/New_York"
}

Request Fields

Field Type Required Description
name string Yes Unique system name (3-63 chars, lowercase alphanumeric with hyphens)
displayName string Yes Human-readable display name
description string No Description of the tenant
maxUsers integer Yes Maximum number of users
maxAnalyst integer Yes Maximum number of analysts
maxCases integer Yes Maximum number of cases
timeZone string No Timezone for the tenant

Tenant Name Requirements

  • 3-63 characters
  • Lowercase alphanumeric with hyphens only
  • No spaces or special characters
  • Must be unique across all tenants

Response (201 Created)

{
  "tenantId": "aabbccdd-1234-1234-1234-123456789012",
  "name": "new-tenant",
  "displayName": "New Tenant Corp",
  "message": "Tenant 'New Tenant Corp' created successfully",
  "storageContainerCreated": true
}

Error Responses

Conflict (409):

{
  "error": "A tenant with name 'new-tenant' already exists"
}

License Limit (429):

{
  "error": "Maximum number of tenants reached. Your license allows 10 tenants.",
  "hint": "Upgrade your license to create more tenants"
}

Validation Error (400):

{
  "error": "Validation failed",
  "validationErrors": [
    "Name must be between 3 and 63 characters",
    "Name can only contain lowercase letters, numbers, and hyphens"
  ]
}

Update Tenant

PUT /api/tenant

Updates an existing tenant's properties. Only provided fields will be updated; null values are ignored.

Request Body

{
  "tenantId": "12345678-1234-1234-1234-123456789012",
  "displayName": "Acme Corporation Updated",
  "description": "Updated description",
  "maxUsers": 100,
  "maxAnalyst": 25,
  "maxCases": 200000,
  "timeZone": "America/New_York",
  "isAcademic": false,
  "preRelease": true,
  "isDisabled": false
}

Request Fields

Field Type Required Description
tenantId GUID Yes The tenant to update
displayName string No New display name (null = no change)
description string No New description (null = no change, "" = clear)
maxUsers integer No Maximum users (null = no change)
maxAnalyst integer No Maximum analysts (null = no change)
maxCases integer No Maximum cases (null = no change, -1 = unlimited)
timeZone string No TimeZone ID (null = no change)
isAcademic boolean No Academic flag (null = no change)
preRelease boolean No Pre-release features (null = no change)
isDisabled boolean No Disable tenant (null = no change)

Note: The tenant name cannot be changed after creation.

Response (200 OK)

{
  "tenantId": "12345678-1234-1234-1234-123456789012",
  "name": "acme-corp",
  "displayName": "Acme Corporation Updated",
  "message": "Tenant 'acme-corp' updated successfully",
  "isDisabled": false
}

Error Responses

Not Found (404):

{
  "error": "Tenant with ID '12345678-1234-1234-1234-123456789012' not found"
}

Validation Error (400):

{
  "error": "Validation failed",
  "validationErrors": ["Display name cannot exceed 255 characters"]
}

Implementation Examples

cURL

# List all tenants
curl -X GET "https://your-mindzie-instance.com/api/tenant?page=1&pageSize=50" \
  -H "Authorization: Bearer YOUR_GLOBAL_API_KEY"

# Get specific tenant by ID
curl -X GET "https://your-mindzie-instance.com/api/tenant/12345678-1234-1234-1234-123456789012" \
  -H "Authorization: Bearer YOUR_GLOBAL_API_KEY"

# Create tenant
curl -X POST "https://your-mindzie-instance.com/api/tenant" \
  -H "Authorization: Bearer YOUR_GLOBAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "new-tenant",
    "displayName": "New Tenant Corp",
    "description": "Test tenant",
    "maxUsers": 50,
    "maxAnalyst": 10,
    "maxCases": 100000
  }'

# Update tenant
curl -X PUT "https://your-mindzie-instance.com/api/tenant" \
  -H "Authorization: Bearer YOUR_GLOBAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tenantId": "12345678-1234-1234-1234-123456789012",
    "displayName": "New Tenant Corp Updated",
    "maxUsers": 100,
    "isDisabled": false
  }'

Python

import requests

BASE_URL = 'https://your-mindzie-instance.com'

class TenantManager:
    def __init__(self, global_api_key):
        """Initialize with a GLOBAL API key (not tenant-specific)."""
        self.headers = {
            'Authorization': f'Bearer {global_api_key}',
            'Content-Type': 'application/json'
        }

    def list_tenants(self, page=1, page_size=50):
        """List all tenants in the system."""
        url = f'{BASE_URL}/api/tenant'
        params = {'page': page, 'pageSize': page_size}
        response = requests.get(url, headers=self.headers, params=params)
        response.raise_for_status()
        return response.json()

    def get_tenant(self, tenant_id):
        """Get a specific tenant by ID."""
        url = f'{BASE_URL}/api/tenant/{tenant_id}'
        response = requests.get(url, headers=self.headers)
        response.raise_for_status()
        return response.json()

    def create_tenant(self, name, display_name, description='', max_users=50,
                      max_analyst=10, max_cases=100000, timezone=None):
        """Create a new tenant."""
        url = f'{BASE_URL}/api/tenant'
        payload = {
            'name': name,
            'displayName': display_name,
            'description': description,
            'maxUsers': max_users,
            'maxAnalyst': max_analyst,
            'maxCases': max_cases
        }
        if timezone:
            payload['timeZone'] = timezone

        response = requests.post(url, json=payload, headers=self.headers)
        response.raise_for_status()
        return response.json()

    def update_tenant(self, tenant_id, display_name=None, description=None,
                      max_users=None, max_analyst=None, is_disabled=None):
        """Update an existing tenant."""
        url = f'{BASE_URL}/api/tenant'
        payload = {'tenantId': tenant_id}
        if display_name is not None:
            payload['displayName'] = display_name
        if description is not None:
            payload['description'] = description
        if max_users is not None:
            payload['maxUsers'] = max_users
        if max_analyst is not None:
            payload['maxAnalyst'] = max_analyst
        if is_disabled is not None:
            payload['isDisabled'] = is_disabled

        response = requests.put(url, json=payload, headers=self.headers)
        response.raise_for_status()
        return response.json()

# Usage
manager = TenantManager('your-global-api-key')

# List all tenants
result = manager.list_tenants()
print(f"Total tenants: {result['totalCount']}")
for tenant in result['tenants']:
    print(f"- {tenant['displayName']} ({tenant['name']})")
    print(f"  Users: {tenant['userCount']}/{tenant['maxUserCount']}")

# Create a new tenant
new_tenant = manager.create_tenant(
    name='test-tenant',
    display_name='Test Tenant',
    description='Created via API',
    max_users=25,
    max_analyst=5,
    max_cases=50000
)
print(f"Created tenant: {new_tenant['tenantId']}")

# Update tenant limits
manager.update_tenant(
    tenant_id=new_tenant['tenantId'],
    max_users=50,
    max_analyst=10
)
print("Tenant limits updated")

JavaScript

const BASE_URL = 'https://your-mindzie-instance.com';

class TenantManager {
  constructor(globalApiKey) {
    this.headers = {
      'Authorization': `Bearer ${globalApiKey}`,
      'Content-Type': 'application/json'
    };
  }

  async listTenants(page = 1, pageSize = 50) {
    const url = `${BASE_URL}/api/tenant?page=${page}&pageSize=${pageSize}`;
    const response = await fetch(url, { headers: this.headers });
    if (!response.ok) throw new Error(`Failed: ${response.status}`);
    return await response.json();
  }

  async getTenant(tenantId) {
    const url = `${BASE_URL}/api/tenant/${tenantId}`;
    const response = await fetch(url, { headers: this.headers });
    if (!response.ok) throw new Error(`Failed: ${response.status}`);
    return await response.json();
  }

  async createTenant(config) {
    const url = `${BASE_URL}/api/tenant`;
    const response = await fetch(url, {
      method: 'POST',
      headers: this.headers,
      body: JSON.stringify(config)
    });
    if (!response.ok) {
      const error = await response.json();
      throw new Error(error.error || `Failed: ${response.status}`);
    }
    return await response.json();
  }

  async updateTenant(tenantId, updates) {
    const url = `${BASE_URL}/api/tenant`;
    const response = await fetch(url, {
      method: 'PUT',
      headers: this.headers,
      body: JSON.stringify({ tenantId, ...updates })
    });
    if (!response.ok) throw new Error(`Failed: ${response.status}`);
    return await response.json();
  }
}

// Usage
const manager = new TenantManager('your-global-api-key');

// List tenants
const tenants = await manager.listTenants();
console.log(`Found ${tenants.totalCount} tenants`);

// Create tenant
const newTenant = await manager.createTenant({
  name: 'new-tenant',
  displayName: 'New Tenant',
  maxUsers: 50,
  maxAnalyst: 10,
  maxCases: 100000
});
console.log(`Created: ${newTenant.tenantId}`);

// Update tenant
await manager.updateTenant(newTenant.tenantId, {
  displayName: 'Updated Tenant Name',
  maxUsers: 100
});

Best Practices

  1. Global API Keys: Only use global API keys for tenant management - they have significant system-wide privileges
  2. License Awareness: Monitor tenant counts against license limits before creating new tenants
  3. Capacity Planning: Set appropriate user and analyst limits based on expected usage
  4. Naming Conventions: Use consistent, lowercase naming with hyphens for tenant names