Multi-tenancy provides data isolation. Each tenant is stored on a separate shard. Data stored in one tenant is not visible to another tenant. If your application serves many different users, multi-tenancy keeps their data private and makes database operations more efficient.

:::callout{intent="info" title="Tenant status renamed in `v1.26`"}
In `v1.26`, the `HOT` status was renamed to `ACTIVE` and the `COLD` status was renamed to `INACTIVE`.
:::

## Enable multi-tenancy

Multi-tenancy is disabled by default. To enable multi-tenancy, set `multiTenancyConfig`in the collection definition:

:::code-group{sync="languages"}
```python title="Python" {6}
from weaviate.classes.config import Configure

multi_collection = client.collections.create(
    name="MultiTenancyCollection",
    # Enable multi-tenancy on the new collection
    multi_tenancy_config=Configure.multi_tenancy(enabled=True)
)
```

```tsindent title="JavaScript/TypeScript" {3}
const result = await client.collections.create({
  name: collectionName,
  multiTenancy: weaviate.configure.multiTenancy({ enabled: true })
})
```

```go title="Go" {4-6}
client.Schema().ClassCreator().
  WithClass(&models.Class{
    Class: "MultiTenancyCollection",
    MultiTenancyConfig: &models.MultiTenancyConfig{
      Enabled: true,
    },
  }).
  Do(ctx)
```

```java title="Java"
client.collections.create("MultiTenancyCollection", col -> col
    .multiTenancy(mt -> mt.enabled(true).autoTenantCreation(true)));
```

```csharp title="C#"
await client.Collections.Create(
    new CollectionCreateParams
    {
        Name = "MultiTenancyCollection",
        MultiTenancyConfig = new MultiTenancyConfig { Enabled = true },
    }
);
```
:::

## Automatically add new tenants

By default, Weaviate returns an error if you try to insert an object into a non-existent tenant. To change this behavior so Weaviate creates a new tenant, set `autoTenantCreation` to `true` in the collection definition.

The auto-tenant feature is available from `v1.25.0` for batch imports, and from `v1.25.2` for single object insertions as well.

Set `autoTenantCreation` when you create the collection, or reconfigure the collection to update the setting as needed.

Automatic tenant creation is useful when you import a large number of objects. Be cautious if your data is likely to have small inconsistencies or typos. For example, the names `TenantOne`, `tenantOne`, and `TenntOne` will create three different tenants.

### Create a collection

:::code-group{sync="languages"}
```python title="Python" {6-9}
from weaviate.classes.config import Configure

multi_collection = client.collections.create(
    name="CollectionWithAutoMTEnabled",
    # Enable automatic tenant creation
    multi_tenancy_config=Configure.multi_tenancy(
        enabled=True,
        auto_tenant_creation=True
    )
)
```

```tsindent title="JavaScript/TypeScript" {5}
const result = await client.collections.create({
  name: collectionName,
  multiTenancy: weaviate.configure.multiTenancy({
    enabled: true,
    autoTenantCreation: true
  })
})
```

```goraw title="Go"
func createClassWithAutoTenantEnabled(ctx context.Context, client *weaviate.Client) {
  class := &models.Class{
    Class: "AutoTenantEnabledOnCreate",
    Properties: []*models.Property{
      {Name: "textProp", DataType: []string{"text"}},
    },
    MultiTenancyConfig: &models.MultiTenancyConfig{
      Enabled:            true,
      AutoTenantCreation: true,
    },
  }
  err := client.Schema().ClassCreator().WithClass(class).Do(ctx)
  if err != nil {
    log.Fatalf("create class: %v", err)
  }
}
```

```java title="Java"
client.collections.create("CollectionWithAutoMTEnabled",
    col -> col.multiTenancy(mt -> mt.autoTenantCreation(true)));
```

```csharp title="C#"
await client.Collections.Create(
    new CollectionCreateParams
    {
        Name = "CollectionWithAutoMTEnabled",
        MultiTenancyConfig = new MultiTenancyConfig
        {
            Enabled = true,
            AutoTenantCreation = true,
        },
    }
);
```

```bash title="cURL" {14-17}
curl localhost:8080/v1/schema -H 'content-type:application/json' -d \
'{
  "class": "Customer",
  "properties": [
    {
      "name": "customer_name",
      "dataType": ["text"]
    },
    {
      "name": "customer_category",
      "dataType": ["text"]
    }
  ],
  "multiTenancyConfig": {
    "enabled": true,
    "autoTenantCreation": true
  }
}'
```
:::

### Update a collection

Use the client to update the auto-tenant creation setting. Auto-tenant is only available for batch inserts.

:::code-group{sync="languages"}
```python title="Python" {5-7}
from weaviate.classes.config import Reconfigure

collection = client.collections.use(collection_name)

collection.config.update(
    multi_tenancy_config=Reconfigure.multi_tenancy(auto_tenant_creation=True)
)
```

```tsindent title="JavaScript/TypeScript"
import { reconfigure } from 'weaviate-client';
```

```go title="Go"
existing, err := client.Schema().ClassGetter().
  WithClassName(class.Class).Do(ctx)
if err != nil {
  log.Fatalf("get existing class %q: %v", class.Class, err)
}

existing.MultiTenancyConfig.AutoTenantCreation = true

err = client.Schema().ClassUpdater().WithClass(existing).Do(ctx)
if err != nil {
  log.Fatalf("enable autotenant: %v", err)
}
```

```java title="Java"
CollectionHandle<Map<String, Object>> collection =
    client.collections.use(collectionName);
collection.config
    .update(col -> col.multiTenancy(mt -> mt.autoTenantCreation(true)));
```

```csharp title="C#"
var collection = client.Collections.Use(collectionName);
await collection.Config.Update(c =>
{
    c.MultiTenancyConfig.AutoTenantCreation = true;
});
```
:::

## Add new tenants manually

To add tenants to a collection, specify the collection and the new tenants. Optionally, specify the tenant activity status as `ACTIVE`(available, default), `INACTIVE` (not available, on disk), or `OFFLOADED` (not available, [offloaded to cloud](../concepts/data.md#tenant-states)).

This example adds `tenantA` to the `MultiTenancyCollection` collection:

::::accordion{title="Additional information"}
Tenant status is available from Weaviate `1.21` onwards.

:::callout{intent="tip" title="Allowable tenant names"}
A tenant name can only contain alphanumeric characters (a-z, A-Z, 0-9), underscore (\_), and hyphen (-), with a length of 4 to 64 characters.
:::
::::

:::code-group{sync="languages"}
```python title="Python" {4-9}
from weaviate.classes.tenants import Tenant

# Add two tenants to the collection
multi_collection.tenants.create(
    tenants=[
        Tenant(name="tenantA"),
        Tenant(name="tenantB"),
    ]
)
```

```tsindent title="JavaScript/TypeScript" {3-6}
const multiCollection = client.collections.use(collectionName);

await multiCollection.tenants.create([
  { name: 'tenantA' },
  { name: 'tenantB' }
])
```

```go title="Go" {3}
client.Schema().TenantsCreator().
  WithClassName("MultiTenancyCollection").
  WithTenants(models.Tenant{Name: "tenantA"}, models.Tenant{Name: "tenantB"}).
  Do(ctx)
```

```java title="Java"
collection.tenants.create(Tenant.active("tenantA"),
    Tenant.active("tenantB"));
```

```csharp title="C#"
await collection.Tenants.Create(["tenantA", "tenantB"]);
```
:::

## List all tenants

List existing tenants in a collection.

This example lists the tenants in the `MultiTenancyCollection` collection:

:::code-group{sync="languages"}
```python title="Python" {3}
multi_collection = client.collections.use("MultiTenancyCollection")

tenants = multi_collection.tenants.get()

print(tenants)
```

```tsindent title="JavaScript/TypeScript" {3}
const multiCollection = client.collections.use(collectionName);

let tenants = await multiCollection.tenants.get()

console.log(tenants)
```

```go title="Go"
tenants, err := client.Schema().TenantsGetter().
  WithClassName("MultiTenancyCollection").
  Do(ctx)
```

```java title="Java"
List<Tenant> tenants = collection.tenants.get();
System.out.println(tenants);
```

```csharp title="C#"
var tenants = await collection.Tenants.List();
foreach (var t in tenants)
    Console.WriteLine(t.Name);
```
:::

## Get tenants by name

Get tenants from a collection by name. Note that non-existent tenant names are ignored in the response.

This example returns `tenantA` and `tenantB` from the `MultiTenancyCollection` collection:

:::code-group{sync="languages"}
```python title="Python" {3-4}
multi_collection = client.collections.use("MultiTenancyCollection")

tenant_names = ["tenantA", "tenantB", "nonExistentTenant"]  # `nonExistentTenant`` does not exist and will be ignored
tenants_response = multi_collection.tenants.get_by_names(tenant_names)

for k, v in tenants_response.items():
    print(k, v)
```

```tsindent title="JavaScript/TypeScript" {3}
const multiCollection = client.collections.use(collectionName);

const tenants = await multiCollection.tenants.getByNames(['tenantA', 'tenantB'])
console.log(tenants)
```

```java title="Java"
List<String> tenantNames =
    Arrays.asList("tenantA", "tenantB", "nonExistentTenant");
List<Tenant> tenants = collection.tenants.get(tenantNames);
System.out.println(tenants);
```

```csharp title="C#"
var tenantNames = new[] { "tenantA", "tenantB", "nonExistentTenant" };
var tenants = await collection.Tenants.List(tenantNames);
foreach (var t in tenants)
    Console.WriteLine(t.Name);
```
:::

## Get one tenant

Get a particular tenant from a collection.

This example returns a tenant from the `MultiTenancyCollection` collection:

:::code-group{sync="languages"}
```python title="Python" {3}
multi_collection = client.collections.use("MultiTenancyCollection")

tenant_obj = multi_collection.tenants.get_by_name(tenant_name)

print(tenant_obj.name)
```

```tsindent title="JavaScript/TypeScript" {3}
const multiCollection = client.collections.use(collectionName);

const tenant = await multiCollection.tenants.getByName('tenantA')
console.log(tenant)
```

```java title="Java"
String tenantName = "tenantA";
Optional<Tenant> tenant = collection.tenants.get(tenantName);
System.out.println(tenant);
```

```csharp title="C#"
string tenantName = "tenantA";
var tenant = await collection.Tenants.Get(tenantName);
Console.WriteLine(tenant?.Name);
```
:::

## Delete tenants

To delete tenants from a collection, specify the collection (e.g. `MultiTenancyCollection`) and the tenants (`tenantB` and `tenantX`). The delete operation ignores tenant names if a named tenant is not a part of the collection.

:::callout{intent="warning" title="Tenant deletion == Tenant data deletion"}
Deleting a tenant deletes all associated objects.
:::

:::code-group{sync="languages"}
```python title="Python" {4}
multi_collection = client.collections.use("MultiTenancyCollection")

# Remove a list of tenants - tenantX will be ignored.
multi_collection.tenants.remove(["tenantB", "tenantX"])
```

```tsindent title="JavaScript/TypeScript" {3-6}
const multiCollection = client.collections.use(collectionName);

await multiCollection.tenants.remove([
  { name: 'tenantB' },
  { name: 'tenantX' }  // tenantX will be ignored
])
```

```go title="Go"
client.Schema().TenantsDeleter().
  WithClassName("MultiTenancyCollection").
  WithTenants("tenantB", "tenantX"). // tenantX will be ignored
  Do(ctx)
```

```java title="Java"
collection.tenants.delete(Arrays.asList("tenantB", "tenantX"));
```

```csharp title="C#"
await collection.Tenants.Delete(new[] { "tenantB", "tenantX" });
```
:::

## Manage tenant states

Change a tenant state between `ACTIVE`, `INACTIVE`, and `OFFLOADED`.

:::code-group{sync="languages"}
```python title="Python" {4-9}
from weaviate.classes.tenants import Tenant, TenantActivityStatus

multi_collection = client.collections.use("MultiTenancyCollection")
multi_collection.tenants.update(tenants=[
    Tenant(
        name="tenantA",
        activity_status=TenantActivityStatus.ACTIVE # INACTIVE, OFFLOADED
    )
])
```

```tsindent title="JavaScript/TypeScript" {3-6}
const multiCollection = client.collections.use(collectionName)

await multiCollection.tenants.update({
  name: 'tenantA',
  activityStatus: 'ACTIVE' // 'INACTIVE', 'OFFLOADED'
})
```

```java title="Java"
String tenantName = "tenantA";
CollectionHandle<Map<String, Object>> collection =
    client.collections.use(collectionName);
collection.tenants.deactivate(tenantName);
collection.tenants.activate(tenantName);
// collection.tenants.offload(tenantName); // Requires S3 offload module
```

```csharp title="C#"
string tenantName = "tenantA";
var multiCollection = client.Collections.Use(collectionName);

// Deactivate
await multiCollection.Tenants.Update([
    new Tenant { Name = tenantName, Status = TenantActivityStatus.Inactive },
]);

// Activate
await multiCollection.Tenants.Update([
    new Tenant { Name = tenantName, Status = TenantActivityStatus.Active },
]);

// Offloading requires S3/warm/cold configuration
```
:::

:::callout{intent="info" title="Learn more"}
See [How-to: Manage tenant states](tenant-states.md) for more hands-on examples, and the [Guide: Manage resources](../starter-guides/managing-resources.md) for more information and strategies to manage **hot**, **warm** and **cold** storage tiers.
:::

## CRUD operations

Multi-tenancy collections require tenant name (e.g. `tenantA`) with each CRUD operation, as shown in the object creation example below.

:::code-group{sync="languages"}
```python title="Python" {4}
multi_collection = client.collections.use("MultiTenancyCollection")

# Get collection specific to the required tenant
multi_tenantA = multi_collection.with_tenant("tenantA")

# Insert an object to tenantA
object_id = multi_tenantA.data.insert(
    properties={
        "question": "This vector DB is OSS & supports automatic property type inference on import"
    }
)
```

```tsindent title="JavaScript/TypeScript" {3,5-7}
const multiCollection = client.collections.use(collectionName);

const multiTenantA = multiCollection.withTenant('tenantA')

await multiTenantA.data.insert({
  question: 'This vector DB is OSS & supports automatic property type inference on import'
})
```

```go title="Go" {6}
object, err := client.Data().Creator().
  WithClassName("MultiTenancyCollection"). // The class to which the object will be added
  WithProperties(map[string]interface{}{
    "question": "This vector DB is OSS & supports automatic property type inference on import",
  }).
  WithTenant("tenantA"). // The tenant to which the object will be added
  Do(ctx)
```

```java title="Java" {1-2}
var jeopardy =
    client.collections.use("JeopardyQuestion").withTenant("tenantA");

var uuid = jeopardy.data.insert(Map.of("question",
    "This vector DB is OSS & supports automatic property type inference on import"))
    .uuid();

System.out.println(uuid); // the return value is the object's UUID
```

```csharp title="C#" {1}
var jeopardy = client.Collections.Use("JeopardyQuestion").WithTenant("tenantA");

var uuid = await jeopardy.Data.Insert(
    new
    {
        question = "This vector DB is OSS & supports automatic property type inference on import",
    }
);

Console.WriteLine(uuid); // the return value is the object's UUID
```
:::

## Search queries

Multi-tenancy collections require the tenant name (e.g. `tenantA`) with each `Get` and `Aggregate` query operation.

:::code-group{sync="languages"}
```python title="Python" {4,7-9}
multi_collection = client.collections.use("MultiTenancyCollection")

# Get collection specific to the required tenant
multi_tenantA = multi_collection.with_tenant("tenantA")

# Query tenantA
result = multi_tenantA.query.fetch_objects(
    limit=2,
)

print(result.objects[0].properties)
```

```tsindent title="JavaScript/TypeScript" {3,5-7}
const multiCollection = client.collections.use(collectionName);

const multiTenantA = multiCollection.withTenant('tenantA')

const objectA = await multiTenantA.query.fetchObjects({
  limit: 2
})

console.log(objectA.objects)
```

```go title="Go" {4}
result, err := client.GraphQL().Get().
  WithClassName("MultiTenancyCollection").
  WithFields(graphql.Field{Name: "question"}).
  WithTenant("tenantA").
  Do(ctx)
```

```java title="Java" {1-2}
var jeopardy =
    client.collections.use("JeopardyQuestion").withTenant("tenantA");

var response = jeopardy.query.fetchObjects(c -> c.limit(2));

for (var o : response.objects()) {
  System.out.println(o.properties());
}
```

```csharp title="C#" {1}
var jeopardy = client.Collections.Use("JeopardyQuestion").WithTenant("tenantA");

var response = await jeopardy.Query.FetchObjects(limit: 2);

foreach (var o in response.Objects)
{
    Console.WriteLine(JsonSerializer.Serialize(o.Properties));
}
```
:::

## Cross-references

:::callout{intent="warning" title="Cross-references and query performance"}
Queries involving cross-references can be slower than queries that do not involve cross-references, especially at scale such as for multiple objects or complex queries.

At the first instance, we strongly encourage you to consider whether you can avoid using cross-references in your data schema. As a scalable AI database, Weaviate is well-placed to perform complex queries with vector, keyword and hybrid searches involving filters. You may benefit from rethinking your data schema to avoid cross-references where possible.

For example, instead of creating separate "Author" and "Book" collections with cross-references, consider embedding author information directly in Book objects and using searches and filters to find books by author characteristics.
:::

A cross-reference can be added from a multi-tenancy collection object to:

- A non-multi-tenancy collection object, or
- An object belonging to the same tenant.

Multi-tenancy collections require the tenant name (e.g. `tenantA`) when creating, updating or deleting cross-references.

:::code-group{sync="languages"}
```python title="Python" {13,16}
from weaviate.classes.config import ReferenceProperty

multi_collection = client.collections.use("MultiTenancyCollection")
# Add the cross-reference property to the multi-tenancy class
multi_collection.config.add_reference(
    ReferenceProperty(
        name="hasCategory",
        target_collection="JeopardyCategory"
    )
)

# Get collection specific to the required tenant
multi_tenantA = multi_collection.with_tenant(tenant="tenantA")

# Add reference from MultiTenancyCollection object to a JeopardyCategory object
multi_tenantA.data.reference_add(
    from_uuid=object_id,  # MultiTenancyCollection object id (a Jeopardy question)
    from_property="hasCategory",
    to=category_id # JeopardyCategory id
)
```

```tsindent title="JavaScript/TypeScript" {4-7,11}
const multiCollection = client.collections.use(collectionName);
// Add the cross-reference property to the multi-tenancy class

await multiCollection.config.addReference({
  name: 'hasCategory',
  targetCollection: 'JeopardyCategory'
})

const multiTenantA = multiCollection.withTenant('tenantA')

await multiTenantA.data.referenceAdd({
  fromUuid: objectId,
  fromProperty: 'hasCategory',
  to: categoryId
})
```

```go title="Go" {13}
// Add the cross-reference property to the multi-tenancy class
client.Schema().PropertyCreator().
  WithClassName("MultiTenancyCollection").
  WithProperty(&models.Property{
    Name:     "hasCategory",
    DataType: []string{"JeopardyCategory"},
  }).
  Do(ctx)

// Create the cross-reference from MultiTenancyCollection object to the JeopardyCategory object
client.Data().ReferenceCreator().
  WithClassName("MultiTenancyCollection").
  WithTenant("tenantA").
  WithID(object.ID.String()). // MultiTenancyCollection object id (a Jeopardy question)
  WithReferenceProperty("hasCategory").
  WithReference(client.Data().ReferencePayloadBuilder().
    WithClassName("JeopardyCategory").
    WithID(category.ID.String()).
    Payload()).
  Do(ctx)
```

```java title="Java"
client.collections.create("JeopardyCategory");
client.collections.create("MultiTenancyCollection",
    col -> col.multiTenancy(mt -> mt.enabled(true)));

var categoryCollection = client.collections.use("JeopardyCategory");
var categoryUuid =
    categoryCollection.data.insert(Map.of("name", "Test Category"));

var multiCollection = client.collections.use("MultiTenancyCollection");
multiCollection.tenants.create(Tenant.active("tenantA"));

var multiTenantA = multiCollection.withTenant("tenantA");
var referenceObject =
    multiTenantA.data.insert(Map.of("title", "Object in Tenant A"));
String objectId = referenceObject.uuid();

multiCollection.config.addReference("hasCategory", "JeopardyCategory");

multiTenantA.data.referenceAdd(objectId, // from_uuid
    "hasCategory", // from_property
    ObjectReference.object(categoryUuid) // to
);
```

```csharp title="C#" {9,20}
var multiCollection = client.Collections.Use("MultiTenancyCollection");
await multiCollection.Tenants.Create(["tenantA"]);
// Add the cross-reference property to the multi-tenancy class
await multiCollection.Config.AddReference(
    Property.Reference("hasCategory", "JeopardyCategory")
);

// Get collection specific to the required tenant
var multiTenantA = multiCollection.WithTenant("tenantA");

// Insert an object to tenantA
var objectId = await multiTenantA.Data.Insert(
    new
    {
        question = "This vector DB is OSS & supports automatic property type inference on import",
    }
);

// Add reference from MultiTenancyCollection object to a JeopardyCategory object
await multiTenantA.Data.ReferenceAdd(
    from: objectId, // MultiTenancyCollection object id (a Jeopardy question)
    fromProperty: "hasCategory",
    to: categoryId // JeopardyCategory id
);
```
:::

## Backups

:::callout{intent="warning" title="Backups do not include offloaded tenants"}
Backups of [multi-tenant collections](../concepts/data.md#multi-tenancy) include both `active` and `inactive` tenants. Inactive tenants are read directly from disk, so you do not need to activate them before creating a backup.

`Offloaded` tenants are not included, because their data is not held on local disk. The tenant status is preserved, but the tenant's data is not part of the backup. [Activate offloaded tenants](#manage-tenant-states) before creating a backup to include their data.

Support for backing up `inactive` tenants was added in `v1.37.0`, and backported to `v1.35.17` and `v1.36.10`. In earlier releases, backups include only `active` tenants.
:::

## Related pages

- [Connect to Weaviate](../connect-to-weaviate/index.md)
- [How to: Manage collections](index.md)
- [References: REST API: Schema](/weaviate/api/rest#tag/schema)
- [Concepts: Data Structure: Multi-tenancy](../concepts/data.md#multi-tenancy)

## Questions and feedback

Have a question or feedback? Here's how to reach us.

::::card-grid
:::card{title="Community Forum" href="https://forum.weaviate.io/c/support" icon="messages-square"}
Ask questions and connect with other developers on our **Community forum**.
:::

:::card{title="Support" href="/guides/support-overview" icon="life-buoy"}
Weaviate Cloud user or customer? Find the right channel on the **Support page**.
:::
::::

## Related pages

- [Agents](./agents-index.md)
- [AI-assisted Weaviate code generation](./ai-assisted-vibe-coding-index.md)
- [APIs](./apis-index.md)
- [Authorization and authentication](./authorization-and-authentication-index.md)
- [Benchmarks](./benchmarks-index.md)
- [Best practices](./best-practices-index.md)
- [Client libraries](./clients-index.md)
- [Client Libraries / SDKs](./client-libraries-index.md)
- [Cloud](./cloud-index.md)
- [Cloud account management](./cloud-account-management-index.md)

# Agent Instructions

This portal answers questions programmatically. To receive a synthesized,
source-cited answer instead of crawling page by page, append the `?ask=`
query parameter to any page URL on this site:

    /guides/quickstart?ask=how+do+I+authenticate

Optional parameters:

- `&goal=<what-you-are-trying-to-do>` steers the answer toward your
  objective (e.g. `&goal=write+a+python+client`).
- `&version=<label>` scopes the answer to a mounted version when the
  portal publishes more than one.

The response is `text/markdown`: the answer followed by a `# Sources` list
of the portal pages it was grounded in. Status codes are the contract:

- `200` — the answer; `402` — the portal owner’s plan or answer credits are
  exhausted (surface this to your operator; do NOT retry); `429` — you are
  rate-limited; back off for the `Retry-After` seconds; `503` — the answer
  lane is temporarily unavailable; fall back to crawling the `.md` pages.

For the full corpus map read `llms.txt` at the site root; for the tool
surface (search + page fetch as MCP tools) see `/mcp`.
