docs/platform/concepts/subscribers.mdx
A subscriber is a notification recipient in Novu, identified by a unique subscriberId. Workflows are triggered for subscribers, and Novu applies channel routing, preferences, and personalization at the subscriber level.
Each subscriber is uniquely identified in Novu by a subscriberId. This ID is defined by your application and serves as the reference point for all subscriber-related operations whether sending messages, retrieving preferences, or managing user data.
Unlike email addresses or phone numbers, which may change or be shared across users, the subscriberId must remain stable and unique within your system. It acts as the anchor that connects a subscriber’s profile, activity history, and delivery settings across all channels and workflows.
A subscriber’s profile holds all relevant data Novu uses to personalize, deliver, and manage notifications. These fields can power dynamic content in your templates, define conditional logic in workflows, and control which channels a subscriber can receive notifications through.
All metadata tied to a subscriber is centralized and accessible via API or dashboard. This structure ensures that when notifications are triggered, Novu references the most up-to-date context for delivery and personalization.
These data includes:
Data stored in the subscriber object that you can easily access in your notification templates. This contains basic info such as email, phone, firstName, locale and others. This data is fixed and structured.
Apart from the above fixed structured user data, any unstructured custom data such as user's address, membershipLevel, preferredTopics, or companySize can also be stored in the data field using key-value pairs.
To deliver messages through push or chat-based channels, Novu also supports storing delivery credentials on the subscriber profile:
deviceTokens: Used to target mobile devices via push notifications.webhookUrl: Used by chat providers such as, Slack, Discord to reach the subscriber.These fields ensure Novu can deliver messages reliably to all supported destinations, even when the channel requires platform-specific configuration.
<Note> Each subscriber channel is limited to a maximum of **100 device tokens**. Requests that exceed this limit will be rejected. See [Platform Limits](/platform/developer/limits) for details. </Note>Learn more about subscriber attributes and schema in the Subscribers API.
Before you can send notifications, a subscriber must exist in Novu. Asides from manually creating a subscriber via the Novu dashboard, Novu supports multiple approaches to subscriber creation depending on your application’s architecture and user lifecycle.
Novu allows you to create a subscriber automatically at the moment a notification is triggered. If the subscriber doesn't already exist, Novu uses the information provided in the workflow trigger to create the subscriber on the fly. If the subscriber exists, Novu updates the stored data with the latest values.
This approach is useful when:
const novu = new Novu({ secretKey: "<YOUR_SECRET_KEY_HERE>" });
await novu.trigger({ to: { subscriberId: "subscriber_unique_identifier", firstName: "Albert", lastName: "Einstein", email: "[email protected]", phone: "+1234567890", }, workflowId: "workflow_identifier", });
</Tab>
<Tab title="Python">
```python
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="workflow_identifier",
to={
"subscriber_id": "subscriber_unique_identifier",
"first_name": "Albert",
"last_name": "Einstein",
"email": "[email protected]",
"phone": "+1234567890",
},
))
novugo "github.com/novuhq/novu-go"
"github.com/novuhq/novu-go/models/components"
)
s := novugo.New(novugo.WithSecurity(os.Getenv("NOVU_SECRET_KEY")))
res, err := s.Trigger(context.Background(), components.TriggerEventRequestDto{ WorkflowID: "workflow_identifier", To: components.CreateToSubscriberPayloadDto(components.SubscriberPayloadDto{ SubscriberID: "subscriber_unique_identifier", FirstName: "Albert", LastName: "Einstein", Email: "[email protected]", Phone: "+1234567890", }), }, nil)
</Tab>
<Tab title="PHP">
```php
use novu;
use novu\Models\Components;
$sdk = novu\Novu::builder()
->setSecurity('<YOUR_SECRET_KEY_HERE>')
->build();
$sdk->trigger(
triggerEventRequestDto: new Components\TriggerEventRequestDto(
workflowId: 'workflow_identifier',
to: new Components\SubscriberPayloadDto(
subscriberId: 'subscriber_unique_identifier',
firstName: 'Albert',
lastName: 'Einstein',
email: '[email protected]',
phone: '+1234567890',
),
),
);
var sdk = new NovuSDK(secretKey: "<YOUR_SECRET_KEY_HERE>");
await sdk.TriggerAsync(triggerEventRequestDto: new TriggerEventRequestDto() { WorkflowId = "workflow_identifier", To = To.CreateSubscriberPayloadDto(new SubscriberPayloadDto() { SubscriberId = "subscriber_unique_identifier", FirstName = "Albert", LastName = "Einstein", Email = "[email protected]", Phone = "+1234567890", }), });
</Tab>
<Tab title="Java">
```java
import co.novu.Novu;
import co.novu.models.components.*;
Novu novu = Novu.builder()
.secretKey("<YOUR_SECRET_KEY_HERE>")
.build();
novu.trigger()
.body(TriggerEventRequestDto.builder()
.workflowId("workflow_identifier")
.to(To2.of(SubscriberPayloadDto.builder()
.subscriberId("subscriber_unique_identifier")
.firstName("Albert")
.lastName("Einstein")
.email("[email protected]")
.phone("+1234567890")
.build()))
.build())
.call();
Alternatively, you can create and store subscriber profiles ahead of time — typically during onboarding, registration, or data sync events. This approach allows you to manage subscriber preferences, enrich profiles, and inspect delivery readiness before any notification is triggered.
<Tabs> <Tab title="Node.js"> ```ts import { Novu } from "@novu/api";const novu = new Novu({ secretKey: "<YOUR_SECRET_KEY_HERE>" });
await novu.subscribers.create({ subscriberId: "subscriber_unique_identifier", firstName: "Albert", lastName: "Einstein", email: "[email protected]", phone: "+1234567890", data: { address: "123 Main St, Anytown, USA", membershipLevel: "Gold", preferredTopics: ["News", "Sports"], }, });
</Tab>
<Tab title="Python">
```python
import os
from novu_py import Novu
with Novu(secret_key=os.getenv("NOVU_SECRET_KEY", "")) as novu:
novu.subscribers.create(create_subscriber_request_dto={
"subscriber_id": "subscriber_unique_identifier",
"first_name": "Albert",
"last_name": "Einstein",
"email": "[email protected]",
"phone": "+1234567890",
"data": {
"address": "123 Main St, Anytown, USA",
"membershipLevel": "Gold",
"preferredTopics": ["News", "Sports"],
},
})
novugo "github.com/novuhq/novu-go"
"github.com/novuhq/novu-go/models/components"
)
s := novugo.New(novugo.WithSecurity(os.Getenv("NOVU_SECRET_KEY")))
res, err := s.Subscribers.Create(context.Background(), components.CreateSubscriberRequestDto{ SubscriberID: "subscriber_unique_identifier", FirstName: "Albert", LastName: "Einstein", Email: "[email protected]", Phone: "+1234567890", Data: map[string]any{ "address": "123 Main St, Anytown, USA", "membershipLevel": "Gold", "preferredTopics": []string{"News", "Sports"}, }, }, nil, nil)
</Tab>
<Tab title="PHP">
```php
use novu;
use novu\Models\Components;
$sdk = novu\Novu::builder()
->setSecurity('<YOUR_SECRET_KEY_HERE>')
->build();
$sdk->subscribers->create(
createSubscriberRequestDto: new Components\CreateSubscriberRequestDto(
subscriberId: 'subscriber_unique_identifier',
firstName: 'Albert',
lastName: 'Einstein',
email: '[email protected]',
phone: '+1234567890',
data: [
'address' => '123 Main St, Anytown, USA',
'membershipLevel' => 'Gold',
'preferredTopics' => ['News', 'Sports'],
],
),
);
var sdk = new NovuSDK(secretKey: "<YOUR_SECRET_KEY_HERE>");
await sdk.Subscribers.CreateAsync(createSubscriberRequestDto: new CreateSubscriberRequestDto() { SubscriberId = "subscriber_unique_identifier", FirstName = "Albert", LastName = "Einstein", Email = "[email protected]", Phone = "+1234567890", Data = new Dictionary<string, object>() { { "address", "123 Main St, Anytown, USA" }, { "membershipLevel", "Gold" }, { "preferredTopics", new List<string> { "News", "Sports" } }, }, });
</Tab>
<Tab title="Java">
```java
import co.novu.Novu;
import co.novu.models.components.CreateSubscriberRequestDto;
import java.util.List;
import java.util.Map;
Novu novu = Novu.builder()
.secretKey("<YOUR_SECRET_KEY_HERE>")
.build();
novu.subscribers().create()
.body(CreateSubscriberRequestDto.builder()
.subscriberId("subscriber_unique_identifier")
.firstName("Albert")
.lastName("Einstein")
.email("[email protected]")
.phone("+1234567890")
.data(Map.ofEntries(
Map.entry("address", "123 Main St, Anytown, USA"),
Map.entry("membershipLevel", "Gold"),
Map.entry("preferredTopics", List.of("News", "Sports"))))
.build())
.call();
This is recommended when:
For scenarios like data migration, syncing large lists, or preloading subscribers, Novu supports bulk creation. This is especially useful when integrating with existing systems or importing subscriber data from external sources. Bulk create method supports creating up to 500 subscribers at once.
<Tabs> <Tab title="Node.js"> ```ts import { Novu } from "@novu/api";const novu = new Novu({ secretKey: "<YOUR_SECRET_KEY_HERE>" });
await novu.subscribers.createBulk({ subscribers: [ { subscriberId: "subscriber_unique_identifier_1", firstName: "Albert", lastName: "Einstein", email: "[email protected]", phone: "+1234567890", }, { subscriberId: "subscriber_unique_identifier_2", firstName: "Isaac", lastName: "Newton", email: "[email protected]", phone: "+1234567891", }, ], });
</Tab>
<Tab title="Python">
```python
import os
from novu_py import Novu
with Novu(secret_key=os.getenv("NOVU_SECRET_KEY", "")) as novu:
novu.subscribers.create_bulk(bulk_subscriber_create_dto={
"subscribers": [
{
"subscriber_id": "subscriber_unique_identifier_1",
"first_name": "Albert",
"last_name": "Einstein",
"email": "[email protected]",
"phone": "+1234567890",
},
{
"subscriber_id": "subscriber_unique_identifier_2",
"first_name": "Isaac",
"last_name": "Newton",
"email": "[email protected]",
"phone": "+1234567891",
},
],
})
novugo "github.com/novuhq/novu-go"
"github.com/novuhq/novu-go/models/components"
)
s := novugo.New(novugo.WithSecurity(os.Getenv("NOVU_SECRET_KEY")))
res, err := s.Subscribers.CreateBulk(context.Background(), components.BulkSubscriberCreateDto{ Subscribers: []components.CreateSubscriberRequestDto{ { SubscriberID: "subscriber_unique_identifier_1", FirstName: "Albert", LastName: "Einstein", Email: "[email protected]", Phone: "+1234567890", }, { SubscriberID: "subscriber_unique_identifier_2", FirstName: "Isaac", LastName: "Newton", Email: "[email protected]", Phone: "+1234567891", }, }, }, nil)
</Tab>
<Tab title="PHP">
```php
use novu;
use novu\Models\Components;
$sdk = novu\Novu::builder()
->setSecurity('<YOUR_SECRET_KEY_HERE>')
->build();
$sdk->subscribers->createBulk(
bulkSubscriberCreateDto: new Components\BulkSubscriberCreateDto(
subscribers: [
new Components\CreateSubscriberRequestDto(
subscriberId: 'subscriber_unique_identifier_1',
firstName: 'Albert',
lastName: 'Einstein',
email: '[email protected]',
phone: '+1234567890',
),
new Components\CreateSubscriberRequestDto(
subscriberId: 'subscriber_unique_identifier_2',
firstName: 'Isaac',
lastName: 'Newton',
email: '[email protected]',
phone: '+1234567891',
),
],
),
);
var sdk = new NovuSDK(secretKey: "<YOUR_SECRET_KEY_HERE>");
await sdk.Subscribers.CreateBulkAsync(bulkSubscriberCreateDto: new BulkSubscriberCreateDto() { Subscribers = new List<CreateSubscriberRequestDto> { new CreateSubscriberRequestDto() { SubscriberId = "subscriber_unique_identifier_1", FirstName = "Albert", LastName = "Einstein", Email = "[email protected]", Phone = "+1234567890", }, new CreateSubscriberRequestDto() { SubscriberId = "subscriber_unique_identifier_2", FirstName = "Isaac", LastName = "Newton", Email = "[email protected]", Phone = "+1234567891", }, }, });
</Tab>
<Tab title="Java">
```java
import co.novu.Novu;
import co.novu.models.components.BulkSubscriberCreateDto;
import co.novu.models.components.CreateSubscriberRequestDto;
import java.util.List;
Novu novu = Novu.builder()
.secretKey("<YOUR_SECRET_KEY_HERE>")
.build();
novu.subscribers().createBulk()
.body(BulkSubscriberCreateDto.builder()
.subscribers(List.of(
CreateSubscriberRequestDto.builder()
.subscriberId("subscriber_unique_identifier_1")
.firstName("Albert")
.lastName("Einstein")
.email("[email protected]")
.phone("+1234567890")
.build(),
CreateSubscriberRequestDto.builder()
.subscriberId("subscriber_unique_identifier_2")
.firstName("Isaac")
.lastName("Newton")
.email("[email protected]")
.phone("+1234567891")
.build()))
.build())
.call();
Subscriber data in Novu can be managed from the Novu dashboard or using the Subscribers API. Both offer full access to view, update, and organize subscriber profiles, but they serve different use cases depending on your requirements.
<Tabs> <Tab title="Dashboard">The Novu dashboard provides a visual interface for exploring and editing subscriber data. It’s useful for:
This is ideal for non technical team members responsible for managing subscribers or teams that want to audit or manage subscriber data without relying on code.
</Tab> <Tab title="API">For programmatic control, the Novu API offers endpoints to create, update, delete, and retrieve subscriber records at scale. It supports:
Use the API when managing subscribers is part of your backend workflows or when changes need to happen dynamically based on user actions.
</Tab> </Tabs>Novu allows each subscriber to define how they want to receive notifications. These preferences influence both the delivery channels and the types of messages a subscriber will receive.
Subscribers can configure preferences that apply across all workflows. These include:
These global settings act as a default and are respected unless explicitly overridden in specific workflows.
In some cases, a subscriber may want to receive certain notifications but only through specific channels. Novu supports fine-grained overrides at the workflow level. This allows you to:
Subscriber data, both structured fields such as firstName, email and custom data can be used to personalize templates. This enables dynamic content such as:
Subscriber preferences and metadata personalization ensure that each subscriber receives relevant, well-targeted messages through the channels they care about.
These are some of the most frequently asked questions about subscribers in Novu.
<AccordionGroup> <Accordion title="Can two subscribers have the same email address?"> Yes, two subscribers can have the same email address, phone number, or any other attributes. However, each subscriber must have a unique subscriberId. </Accordion> <Accordion title="Do I have to use subscriberId as same as the system userId?"> No, you don't need to use the same subscriberId as the system userId. You can use any unique ID as subscriberId. Novu recommends using userId as subscriberId to avoid any confusion. Some of our customers use a pattern like `auth0|userId` as a value for `subscriberId`. </Accordion> <Accordion title="Can a notification be sent without adding a subscriber?"> No, a notification cannot be sent without adding a subscriber. A subscriber is an entity to which notification messages are sent. You must add a subscriber to Novu before triggering the workflow. </Accordion> <Accordion title="How do I migrate millions of users to Novu?"> To migrate millions of users to Novu, use the [Bulk Create Subscribers](/api-reference/subscribers/bulk-create-subscribers) API endpoint. This endpoint allows you to create multiple subscribers in bulk. </Accordion> <Accordion title="Can subscriber credentials for chat and push channels be added when creating a new subscriber?"> Subscriber credentials for Push and Chat channel providers can be added while creating a new subscriber using the `channels` field in the [create subscriber](/api-reference/subscribers/create-a-subscriber) request payload. </Accordion> </AccordionGroup>