> ## Documentation Index
> Fetch the complete documentation index at: https://docs.novu.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Multi-tenancy

> Learn how to implement multi-tenant notifications in Novu using contexts to isolate Inbox feeds, scope preferences, and personalize workflow content per tenant.

Multi-tenancy is a common requirement for applications that serve multiple organizations, workspaces, or customers from a single Novu project. Tenants are also commonly called workspaces or organizations.

Typical multi-tenancy goals include:

* Grouping each subscriber's Inbox feed by tenant
* Scoping notification preferences per tenant
* Adjusting notification content and branding per tenant

Novu supports multi-tenancy through [contexts](/platform/concepts/contexts). Use a `tenant` context to define tenant boundaries instead of duplicating subscribers, prefixing subscriber IDs, or maintaining separate workflows per tenant.

<Note>
  New to contexts? Start with [Contexts](/platform/concepts/contexts) to understand how context scoping, persistence, and Inbox filtering work.
</Note>

## How multi-tenancy works in Novu

Multi-tenancy in Novu is built on top of contexts. A `tenant` context identifies which organization a notification belongs to.

When you trigger a workflow with a tenant context:

* Notifications are associated with that tenant
* The same subscriber can receive different notifications per tenant
* [Inbox](/platform/inbox) preferences are scoped to the subscriber and tenant context combination

When you initialize the Inbox with the same tenant context, the subscriber sees only notifications for that tenant.

## Implement multi-tenancy with contexts

<Steps>
  <Step title="Define a tenant context">
    A tenant context identifies a specific tenant. Use a simple string for the tenant ID, or a rich object when you need metadata such as company name, logo, or plan type.

    ```ts theme={null}
    const tenantContext = {
      tenant: {
        id: "acme-corp",
        data: {
          name: "Acme Corporation",
          plan: "enterprise",
          logo: "https://cdn.acme.com/logo.png",
        },
      },
    };
    ```

    Contexts are auto-created on first use. Manage them from the **Contexts** section in the Novu dashboard or through the [Contexts API](/api-reference/contexts/context-schema).
  </Step>

  <Step title="Trigger workflows with a tenant context">
    Pass the tenant context when triggering a workflow. Keep the subscriber ID stable—use your application's user ID, not a tenant-prefixed value.

    <Tabs>
      <Tab title="Node.js">
        ```ts theme={null}
        import { Novu } from "@novu/api";

        const novu = new Novu({ secretKey: "<YOUR_SECRET_KEY_HERE>" });

        await novu.trigger({
          workflowId: "invoice-paid",
          to: { subscriberId: "user-123" },
          payload: { amount: "$250" },
          context: {
            tenant: {
              id: "acme-corp",
              data: {
                name: "Acme Corporation",
                plan: "enterprise",
              },
            },
          },
        });
        ```
      </Tab>

      <Tab title="Python">
        ```python theme={null}
        import os
        import novu_py
        from novu_py import Novu

        with Novu(secret_key=os.getenv("NOVU_SECRET_KEY", "")) as novu:
            novu.trigger(trigger_event_request_dto=novu_py.TriggerEventRequestDto(
                workflow_id="invoice-paid",
                to={"subscriber_id": "user-123"},
                payload={"amount": "$250"},
                context={
                    "tenant": {
                        "id": "acme-corp",
                        "data": {
                            "name": "Acme Corporation",
                            "plan": "enterprise",
                        },
                    },
                },
            ))
        ```
      </Tab>

      <Tab title="Go">
        ```go theme={null}
        import (
            "context"
            "os"

            novugo "github.com/novuhq/novu-go"
            "github.com/novuhq/novu-go/models/components"
        )

        s := novugo.New(novugo.WithSecurity(os.Getenv("NOVU_SECRET_KEY")))

        s.Trigger(context.Background(), components.TriggerEventRequestDto{
            WorkflowID: "invoice-paid",
            To: components.CreateToSubscriberPayloadDto(components.SubscriberPayloadDto{
                SubscriberID: "user-123",
            }),
            Payload: map[string]any{"amount": "$250"},
            Context: map[string]any{
                "tenant": map[string]any{
                    "id": "acme-corp",
                    "data": map[string]any{
                        "name": "Acme Corporation",
                        "plan": "enterprise",
                    },
                },
            },
        }, nil)
        ```
      </Tab>

      <Tab title="PHP">
        ```php theme={null}
        use novu;
        use novu\Models\Components;

        $sdk = novu\Novu::builder()
            ->setSecurity('<YOUR_SECRET_KEY_HERE>')
            ->build();

        $sdk->trigger(
            triggerEventRequestDto: new Components\TriggerEventRequestDto(
                workflowId: 'invoice-paid',
                to: new Components\SubscriberPayloadDto(subscriberId: 'user-123'),
                payload: ['amount' => '$250'],
                context: [
                    'tenant' => [
                        'id' => 'acme-corp',
                        'data' => [
                            'name' => 'Acme Corporation',
                            'plan' => 'enterprise',
                        ],
                    ],
                ],
            ),
        );
        ```
      </Tab>

      <Tab title=".NET">
        ```csharp theme={null}
        using Novu;
        using Novu.Models.Components;
        using System.Collections.Generic;

        var sdk = new NovuSDK(secretKey: "<YOUR_SECRET_KEY_HERE>");

        await sdk.TriggerAsync(triggerEventRequestDto: new TriggerEventRequestDto() {
            WorkflowId = "invoice-paid",
            To = To.CreateSubscriberPayloadDto(new SubscriberPayloadDto() {
                SubscriberId = "user-123",
            }),
            Payload = new Dictionary<string, object>() {
                { "amount", "$250" },
            },
            Context = new Dictionary<string, object>() {
                { "tenant", new Dictionary<string, object>() {
                    { "id", "acme-corp" },
                    { "data", new Dictionary<string, object>() {
                        { "name", "Acme Corporation" },
                        { "plan", "enterprise" },
                    } },
                } },
            },
        });
        ```
      </Tab>

      <Tab title="Java">
        ```java theme={null}
        import co.novu.Novu;
        import co.novu.models.components.*;
        import java.util.Map;

        Novu novu = Novu.builder()
            .secretKey("<YOUR_SECRET_KEY_HERE>")
            .build();

        novu.trigger()
            .body(TriggerEventRequestDto.builder()
                .workflowId("invoice-paid")
                .to(To2.of(SubscriberPayloadDto.builder()
                    .subscriberId("user-123")
                    .build()))
                .payload(Map.of("amount", "$250"))
                .context(Map.ofEntries(
                    Map.entry("tenant", TriggerEventRequestDtoContextUnion.of(
                        TriggerEventRequestDtoContext2.builder()
                            .id("acme-corp")
                            .data(Map.ofEntries(
                                Map.entry("name", "Acme Corporation"),
                                Map.entry("plan", "enterprise")))
                            .build()))))
                .build())
            .call();
        ```
      </Tab>

      <Tab title="cURL">
        ```bash theme={null}
        curl -L -g -X POST 'https://api.novu.co/v1/events/trigger' \
        -H 'Content-Type: application/json' \
        -H 'Accept: application/json' \
        -H 'Authorization: ApiKey <NOVU_SECRET_KEY>' \
        -d '{
            "name": "invoice-paid",
            "to": { "subscriberId": "user-123" },
            "payload": { "amount": "$250" },
            "context": {
                "tenant": {
                    "id": "acme-corp",
                    "data": {
                        "name": "Acme Corporation",
                        "plan": "enterprise"
                    }
                }
            }
        }'
        ```
      </Tab>
    </Tabs>
  </Step>

  <Step title="Filter the Inbox by tenant">
    Pass the same tenant context to the Inbox so each subscriber sees only notifications for their active tenant.

    ```tsx theme={null}
    import { Inbox } from "@novu/react";

    function TenantInbox({ userId, tenant }) {
      return (
        <Inbox
          applicationIdentifier="YOUR_APPLICATION_IDENTIFIER"
          subscriberId={userId}
          context={{
            tenant: {
              id: tenant.id,
              data: tenant.data,
            },
          }}
        />
      );
    }
    ```

    When a subscriber switches tenants, re-render the Inbox with the new context. Novu refetches notifications and reconnects the WebSocket scope.

    <Note>
      Secure tenant context in production with `contextHash`. See [Inbox with context](/platform/inbox/configuration/inbox-with-context).
    </Note>
  </Step>

  <Step title="Personalize content per tenant">
    Use tenant metadata in templates with the `{{context}}` helper:

    ```liquid theme={null}
    Hello {{subscriber.firstName}}, your {{context.tenant.data.plan}} plan
    on {{context.tenant.data.name}} just renewed.
    ```

    You can also branch workflow logic on tenant data in [step conditions](/platform/workflow/add-and-configure-steps/step-conditions). See [Contexts in workflows](/platform/workflow/advanced-features/contexts/contexts-in-workflows).
  </Step>
</Steps>

## Tenant context in the Inbox

The Inbox uses exact-match context filtering. The tenant context on `<Inbox />` must match the context used at trigger time.

| Workflow context                  | Inbox context                     | Displayed? |
| --------------------------------- | --------------------------------- | ---------- |
| `{ tenant: { id: "acme-corp" } }` | `{ tenant: { id: "acme-corp" } }` | Yes        |
| `{ tenant: { id: "acme-corp" } }` | `{ tenant: { id: "globex" } }`    | No         |
| `{ tenant: { id: "acme-corp" } }` | `{}`                              | No         |

Each subscriber gets a separate Inbox feed and preference configuration per tenant context.

## Frequently asked questions

<AccordionGroup>
  <Accordion title="Can I use a different delivery provider for each tenant?">
    Currently, Novu does not support using a different delivery provider per tenant. Contact [support@novu.co](mailto:support@novu.co) if this is required for your use case.
  </Accordion>

  <Accordion title="Can subscribers manage different preferences per tenant?">
    Yes. When you scope the Inbox with a tenant context, preferences are stored per subscriber and tenant context combination. A subscriber can enable email for one tenant and disable it for another.
  </Accordion>

  <Accordion title="Can I combine tenant context with other context types?">
    Yes. You can pass up to five context keys per trigger—for example, `tenant` and `app` together. The Inbox must declare the same combined context to display those notifications. See [Contexts](/platform/concepts/contexts).
  </Accordion>
</AccordionGroup>

## Related guides

<CardGroup cols={2}>
  <Card title="Contexts" icon="layers" href="/platform/concepts/contexts">
    Learn what contexts are and how they work across Novu.
  </Card>

  <Card title="Multi-tenancy in the Inbox" icon="inbox" href="/platform/inbox/advanced-features/multi-tenancy">
    End-to-end Inbox setup for multi-tenant applications.
  </Card>

  <Card title="Manage contexts" icon="database" href="/platform/workflow/advanced-features/contexts/manage-contexts">
    Create, update, and delete tenant contexts through the API.
  </Card>

  <Card title="Inbox with context" icon="shield" href="/platform/inbox/configuration/inbox-with-context">
    Secure tenant context with `contextHash`.
  </Card>
</CardGroup>
