Tenant Deletion

Permanently delete a tenant and all related data. This operation requires triple verification for safety.

IMPORTANT: All endpoints on this page require a Global API Key. This is a dangerous operation that cannot be undone.


Delete Tenant

DELETE /api/tenant

Permanently deletes a tenant and all related data. Requires triple verification for safety.

WARNING

This operation is IRREVERSIBLE. All tenant data will be permanently deleted including:

  • All projects and their datasets
  • All investigations, notebooks, and dashboards
  • Blob storage container and files
  • Database records and settings
  • User assignments to the tenant (users themselves are not deleted)

Always export important data before deleting a tenant.


Request Body

{
  "tenantId": "12345678-1234-1234-1234-123456789012",
  "name": "acme-corp",
  "displayName": "Acme Corporation"
}

Triple Verification

All three identifiers must match exactly for the deletion to proceed:

Field Type Required Description
tenantId GUID Yes Tenant ID to delete
name string Yes Tenant name (must match exactly)
displayName string Yes Display name (must match exactly)

This triple verification prevents accidental deletions by requiring you to know and confirm all three identifiers.


Response (200 OK)

{
  "message": "Tenant 'Acme Corporation' deleted successfully",
  "tenantName": "acme-corp",
  "tenantDisplayName": "Acme Corporation",
  "storageContainerDeleted": true
}

Response Fields

Field Type Description
message string Success confirmation message
tenantName string The deleted tenant's system name
tenantDisplayName string The deleted tenant's display name
storageContainerDeleted boolean Whether the blob storage was deleted

Error Responses

Not Found (404)

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

Verification Failed (400)

When the tenant name doesn't match:

{
  "error": "Tenant name 'wrong-name' does not match the tenant with ID '12345678-1234-1234-1234-123456789012'. Expected 'acme-corp'.",
  "hint": "All three identifiers (ID, name, display name) must match exactly for safety"
}

When the display name doesn't match:

{
  "error": "Display name 'Wrong Name' does not match the tenant with ID '12345678-1234-1234-1234-123456789012'. Expected 'Acme Corporation'.",
  "hint": "All three identifiers (ID, name, display name) must match exactly for safety"
}

Unauthorized (401)

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

Implementation Examples

cURL

# Delete tenant (requires triple verification)
curl -X DELETE "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",
    "name": "test-tenant",
    "displayName": "Test Tenant"
  }'

Python

import requests

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

class TenantDeleter:
    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 get_tenant(self, tenant_id):
        """Get tenant details for verification."""
        url = f'{BASE_URL}/api/tenant/{tenant_id}'
        response = requests.get(url, headers=self.headers)
        response.raise_for_status()
        return response.json()

    def delete_tenant(self, tenant_id, name, display_name, confirm=False):
        """
        Delete a tenant with triple verification.

        Args:
            tenant_id: The tenant GUID
            name: The tenant system name (must match exactly)
            display_name: The tenant display name (must match exactly)
            confirm: Set to True to actually perform deletion
        """
        if not confirm:
            # Verification mode - check that values match
            tenant = self.get_tenant(tenant_id)

            errors = []
            if tenant['name'] != name:
                errors.append(f"Name mismatch: expected '{tenant['name']}', got '{name}'")
            if tenant['displayName'] != display_name:
                errors.append(f"Display name mismatch: expected '{tenant['displayName']}', got '{display_name}'")

            if errors:
                raise ValueError("Verification failed:\n" + "\n".join(errors))

            print(f"Verification passed for tenant '{display_name}' ({name})")
            print(f"  - ID: {tenant_id}")
            print(f"  - Users: {tenant.get('userCount', 'N/A')}")
            print(f"  - Cases: {tenant.get('caseCount', 'N/A')}")
            print("\nCall with confirm=True to proceed with deletion.")
            return None

        # Perform the deletion
        url = f'{BASE_URL}/api/tenant'
        payload = {
            'tenantId': tenant_id,
            'name': name,
            'displayName': display_name
        }

        response = requests.delete(url, json=payload, headers=self.headers)

        if response.ok:
            return response.json()
        elif response.status_code == 404:
            raise Exception(f'Tenant not found: {tenant_id}')
        elif response.status_code == 400:
            error = response.json()
            raise Exception(f"Verification failed: {error.get('error', 'Unknown error')}")
        else:
            raise Exception(f'Failed to delete tenant: {response.text}')

# Usage - Safe deletion workflow
deleter = TenantDeleter('your-global-api-key')

tenant_id = '12345678-1234-1234-1234-123456789012'
tenant_name = 'test-tenant'
display_name = 'Test Tenant'

# Step 1: Verify (no deletion occurs)
try:
    deleter.delete_tenant(tenant_id, tenant_name, display_name, confirm=False)
except ValueError as e:
    print(f"Verification failed: {e}")
    exit(1)

# Step 2: Confirm deletion (uncomment when ready)
# result = deleter.delete_tenant(tenant_id, tenant_name, display_name, confirm=True)
# print(f"Deleted: {result['message']}")

JavaScript

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

class TenantDeleter {
  constructor(globalApiKey) {
    this.headers = {
      'Authorization': `Bearer ${globalApiKey}`,
      'Content-Type': 'application/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 to get tenant: ${response.status}`);
    return await response.json();
  }

  async deleteTenant(tenantId, name, displayName, confirm = false) {
    if (!confirm) {
      // Verification mode
      const tenant = await this.getTenant(tenantId);

      const errors = [];
      if (tenant.name !== name) {
        errors.push(`Name mismatch: expected '${tenant.name}', got '${name}'`);
      }
      if (tenant.displayName !== displayName) {
        errors.push(`Display name mismatch: expected '${tenant.displayName}', got '${displayName}'`);
      }

      if (errors.length > 0) {
        throw new Error('Verification failed:\n' + errors.join('\n'));
      }

      console.log(`Verification passed for tenant '${displayName}' (${name})`);
      console.log(`  - ID: ${tenantId}`);
      console.log(`  - Users: ${tenant.userCount || 'N/A'}`);
      console.log('\nCall with confirm=true to proceed with deletion.');
      return null;
    }

    // Perform the deletion
    const url = `${BASE_URL}/api/tenant`;
    const response = await fetch(url, {
      method: 'DELETE',
      headers: this.headers,
      body: JSON.stringify({ tenantId, name, displayName })
    });

    if (response.ok) {
      return await response.json();
    }

    const error = await response.json();
    throw new Error(error.error || `Delete failed: ${response.status}`);
  }
}

// Usage - Safe deletion workflow
const deleter = new TenantDeleter('your-global-api-key');

const tenantId = '12345678-1234-1234-1234-123456789012';
const tenantName = 'test-tenant';
const displayName = 'Test Tenant';

// Step 1: Verify (no deletion occurs)
try {
  await deleter.deleteTenant(tenantId, tenantName, displayName, false);
} catch (e) {
  console.error(`Verification failed: ${e.message}`);
  process.exit(1);
}

// Step 2: Confirm deletion (uncomment when ready)
// const result = await deleter.deleteTenant(tenantId, tenantName, displayName, true);
// console.log(`Deleted: ${result.message}`);

Safety Best Practices

Before Deletion

  1. Export All Data: Use the Project API to export all projects as .mpz files
  2. Verify Users: Check which users are assigned and notify them
  3. Document: Record what is being deleted and why for audit purposes
  4. Double-Check: Verify the tenant ID, name, and display name are correct

During Deletion

  1. Use the Verification Pattern: Call the delete endpoint in verify-only mode first
  2. Check Response: Verify the response shows the correct tenant information
  3. Confirm Deliberately: Only pass confirm=True after manual verification

After Deletion

  1. Verify Deletion: Confirm the tenant no longer appears in the tenant list
  2. Check Users: Verify affected users can no longer access the deleted tenant
  3. Update Documentation: Record the deletion in your system documentation

Alternative: Disable Instead of Delete

Consider disabling a tenant instead of deleting it to preserve data while preventing access:

# Disable tenant (preserves data)
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",
    "isDisabled": true
  }'

Disabled tenants:

  • Users cannot log in to the tenant
  • All data is preserved
  • Can be re-enabled later by setting isDisabled: false
  • Appear in the tenant list with isDisabled: true

This is often a safer choice than permanent deletion.