# Organizations

Before we go into how Novel handles multi-tenancy, We'd love for you to check out this wonderful article by Andrew Culver, creator of Bullettrain, a rails starter kit

{% embed url="<https://blog.bullettrain.co/teams-should-be-an-mvp-feature/>" %}
If you prefer to use rails, check out bullettrain.co instead
{% endembed %}

Novel comes with multi-tenancy enabled by default. This allows your SaaS customers to scope their actions based on an organization they belong with.

What this means is that any feature related to your SaaS should be developed with multi-tenancy in mind.

## Data Model

There are 3 data models relevant to multi-tenancy

1. [Organization ](https://github.com/madewithnovel/novel/blob/main/packages/novel/models/organizations/base.ts)- This is the organization that can have multiple users.
2. [Accounts ](https://github.com/madewithnovel/novel/blob/main/packages/novel/models/accounts/base.ts)- A user account
3. [Organization Membership](https://github.com/madewithnovel/novel/blob/main/packages/novel/models/organization-members/base.ts) - The membership detail of an account to an organization.

Novel implements a similar data model to the article written by Andrew Culver. A big difference is that subscriptions are by default, tied to an organization instead of the membership.

## Signup

Novel will create an organization and an acount during sign up. This will also create a stripe customer and create a subscription for it against the organization created.

When the user logs in the next time, the organization will be attached to the session.

Reference: <https://github.com/madewithnovel/novel/blob/main/app/features/lifecycle/signup.ts>

## Sessions

{% embed url="<https://docs.novel.dev/novel-server/sessions>" %}

Sessions in Novel include both the currently logged in user and the organization it is currently using. They are accessible using the `request.org` and `request.account` property of the current request.

{% hint style="info" %}
**Important**

Any action you want to perform should only apply to the current account and organization of that session.
{% endhint %}

### Switch Organizations

You are able to switch organizations in the same session by using the [**Switch Organization**](https://docs.novel.dev/novel-server/novel-api/api-reference#api-v1-session-switch) endpoint in the API reference below.

{% embed url="<https://docs.novel.dev/novel-server/novel-api/api-reference#api-v1-session-switch>" %}

## Subscriptions

When the user is created, an organization is created and a stripe customer is created along with it.

This is important so that there is representation of the organization in stripe.

Any changes to the subscription is attached to the organization only. This can only be done by users with the correct permission.

An example of this can be found here: <https://github.com/madewithnovel/novel/blob/main/app/api/internal/v1/subscriptions-subscribe/index.ts>

<pre class="language-typescript" data-title="/app/api/internal/v1/subscriptions-subscribe/index.ts" data-line-numbers><code class="lang-typescript">import * as upgrade from 'app/features/lifecycle/upgrade';

export default async function Route (instance) {
	instance.authenticated();
	instance.post('/api/v1/subscription/upgrade', handler);

	async function handler (request, reply) {
		await request.throttle();
		<a data-footnote-ref href="#user-content-fn-1">await request.can('write', request.org.id);</a>
		const orgId = request.org.id;

		/**
		 * See the implementation of the strategy in /app/features/lifecycle/upgrade.ts
		 *
		 * These may have additional requirements during creation like upfront payment
		 * and storing data. You may pass these to your upgrade lifecycle.
		 */

		const { plan, intent, method, interval } = request.body;
		await upgrade.start(request.session.id, { customer: orgId, plan, intent, method, interval });

		reply.status(204);
	}
}
</code></pre>

## Actions

It is important to always check the association of the current user against its membership of the target organization it is trying to perform an action on.

This is available with the Organizations Model.

{% content-ref url="/pages/YnCI9EvKEucRMliY1P0r" %}
[Authorization](/novel-server/authorization.md)
{% endcontent-ref %}

<pre class="language-typescript" data-title="app/api/v1/your-route.ts" data-line-numbers><code class="lang-typescript">import * as Organization from 'novel/organization';

export default async function Route (instance) {
	instance.authenticated();
	instance.cached();
	instance.get('/api/v1/organizations', handler);

	async function handler (request) {
		await request.throttle();
		await request.can('read', request.org.id);
		<a data-footnote-ref href="#user-content-fn-2">await Organization.isMember(request.org.id, request.account.id);</a>
		const organizations = await Organization.isMemberOf(request.account.id);
		return { organizations };
	}
}
</code></pre>

## Activities

An organization may have activities recorded against actions made to it.

This is stored in the `organization_events` model. It is also available under the `novel/activity` module.

{% code title="feature.ts" lineNumbers="true" %}

```typescript
import * as activity from 'novel/activity';

await activity.org('SOMETHING_NEW', 'Something happened');
```

{% endcode %}

This will get the organization ID through the request context.

## Single-Tenancy

{% hint style="warning" %}
Single tenancy is not fully supported as of release 2025.1.0.
{% endhint %}

It is possible to do this however by removing the organization related endpoints from the `/app/api/v1`directory.

This will force the user to sign up and interact with only organization.

If you feel that this is a feature that you would like to see, send us an email at <hello@novel.dev>.

## Changelog

* 2024-12-20 - Initial Documentation

[^1]: This makes sure that the user is allowed to perform an action against this organization.

[^2]: You need to check whether the account is a member of that organization before performing an action.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.novel.dev/novel-server/organizations.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
