docs/platform/integrations/email.mdx
Email providers are the services that deliver notifications to your subscribers’ email. You must set up each provider individually in the Novu dashboard to enable delivery through the Email channel.
Novu provides a unified integration layer that connects your workflows to these email providers. Once you’ve added your integrations, Novu handles routing and delivery automatically, sending each email through the correct provider without requiring additional setup. This allows you to manage multiple email integrations and switch providers when needed.
Here’s the typical flow for sending an email notification through Novu:
<Steps> <Step>Start by adding an email provider in the Integration Store on your Novu dashboard. You can connect one or more integrations for different or the same providers.
To learn how to add an email provider, refer to the guide for the supported providers.
</Step> <Step> ### Add the Email channel to your workflowNext, include an Email step in a workflow. This step defines when and how an email should be sent as part of your notification workflow. </Step> <Step>
Within the Email step, you can design your message using the built-in visual editor or code editor. Novu supports dynamic data from your payload, so each message can be personalized for the subscriber. </Step> <Step>
Novu automatically sends the notification to the email address stored on the subscriber's profile. This profile is update either using the Novu dashboard or API. </Step> <Step>
Trigger the workflow from your application code by sending an event to Novu. Novu automatically:
When you add an email provider in the Integration Store, you configure settings that are common to all email providers, as well as credentials specific to that provider.
Novu asks for two default settings for any email provider you connect:
These are the default values used for every email sent via this integration. You can override them when triggering a workflow if needed.
You must provide credentials specific to your email provider for a successful integration.
<Note> For detailed setup guides for each integration, refer to the [Supported Providers](/platform/integrations/email#supported-providers) list at the bottom of this page. </Note>You can control advanced email features at runtime by passing data in your trigger call. This lets you send attachments, override default settings, or target a specific provider for a single notification.
You can send attachments by passing an attachments array in the payload of your trigger. The attachment file can be provided as a buffer or a base64 encoded string.
There is a total limit of 20MB for all attachments included in an email.
<Tabs> <Tab title="Node.js"> ```javascript import { Novu } from '@novu/api';const novu = new Novu({ secretKey: "<NOVU_SECRET_KEY>", // Required if using EU region // serverURL: "https://eu.api.novu.co", });
await novu.trigger({ workflowId: "workflowId", to: { subscriberId: "subscriberId", }, payload: { attachments: [ { // buffer format file: fs.readFileSync(__dirname + '/data/novu.jpeg'), name: 'novu.jpeg', mime: 'image/jpeg', }, { // base64 format file: 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAFUlEQVR42mNkYPhfz0AEYBxVSF+FAP5FDvcfRYWgAAAAAElFTkSuQmCC', name: 'blue.png', mime: 'image/png', } ], }, });
</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="workflowId",
to={"subscriber_id": "subscriberId"},
payload={
"attachments": [
{
"file": "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAFUlEQVR42mNkYPhfz0AEYBxVSF+FAP5FDvcfRYWgAAAAAElFTkSuQmCC",
"name": "blue.png",
"mime": "image/png",
},
{
"file": "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII",
"name": "transparent.png",
"mime": "image/png",
},
],
},
))
novugo "github.com/novuhq/novu-go"
"github.com/novuhq/novu-go/models/components"
)
s := novugo.New(novugo.WithSecurity(os.Getenv("NOVU_SECRET_KEY")))
_, err := s.Trigger(context.Background(), components.TriggerEventRequestDto{ WorkflowID: "workflowId", To: components.CreateToSubscriberPayloadDto(components.SubscriberPayloadDto{ SubscriberID: "subscriberId", }), Payload: map[string]any{ "attachments": []map[string]any{ { "file": "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAFUlEQVR42mNkYPhfz0AEYBxVSF+FAP5FDvcfRYWgAAAAAElFTkSuQmCC", "name": "blue.png", "mime": "image/png", }, { "file": "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII", "name": "transparent.png", "mime": "image/png", }, }, }, }, nil)
</Tab>
<Tab title="PHP">
```php
use novu;
use novu\Models\Components;
$sdk = novu\Novu::builder()
->setSecurity('<NOVU_SECRET_KEY>')
->build();
$sdk->trigger(
triggerEventRequestDto: new Components\TriggerEventRequestDto(
workflowId: 'workflowId',
to: new Components\SubscriberPayloadDto(subscriberId: 'subscriberId'),
payload: [
'attachments' => [
[
'file' => 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAFUlEQVR42mNkYPhfz0AEYBxVSF+FAP5FDvcfRYWgAAAAAElFTkSuQmCC',
'name' => 'blue.png',
'mime' => 'image/png',
],
[
'file' => 'iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII',
'name' => 'transparent.png',
'mime' => 'image/png',
],
],
],
),
);
var sdk = new NovuSDK(secretKey: "<NOVU_SECRET_KEY>");
await sdk.TriggerAsync(triggerEventRequestDto: new TriggerEventRequestDto() { WorkflowId = "workflowId", To = To.CreateSubscriberPayloadDto(new SubscriberPayloadDto() { SubscriberId = "subscriberId", }), Payload = new Dictionary<string, object>() { { "attachments", new List<Dictionary<string, object>>() { new Dictionary<string, object>() { { "file", "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAFUlEQVR42mNkYPhfz0AEYBxVSF+FAP5FDvcfRYWgAAAAAElFTkSuQmCC" }, { "name", "blue.png" }, { "mime", "image/png" }, }, new Dictionary<string, object>() { { "file", "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII" }, { "name", "transparent.png" }, { "mime", "image/png" }, }, } }, }, });
</Tab>
<Tab title="Java">
```java
import co.novu.Novu;
import co.novu.models.components.*;
import java.util.List;
import java.util.Map;
Novu novu = Novu.builder()
.secretKey("<NOVU_SECRET_KEY>")
.build();
novu.trigger()
.body(TriggerEventRequestDto.builder()
.workflowId("workflowId")
.to(To2.of(SubscriberPayloadDto.builder()
.subscriberId("subscriberId")
.build()))
.payload(Map.of(
"attachments", List.of(
Map.of(
"file", "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAFUlEQVR42mNkYPhfz0AEYBxVSF+FAP5FDvcfRYWgAAAAAElFTkSuQmCC",
"name", "blue.png",
"mime", "image/png"),
Map.of(
"file", "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII",
"name", "transparent.png",
"mime", "image/png"))))
.build())
.call();
You can override the email settings for a single trigger by passing an overrides object. This lets you override the following fields:
bccccfrom addressreplyToreplaceToRecipientsenderNametextto addressconst novu = new Novu({ secretKey: "<NOVU_SECRET_KEY>", // Required if using EU region // serverURL: "https://eu.api.novu.co", });
await novu.trigger({
workflowId: "workflowId",
to: {
subscriberId: "subscriberId",
},
overrides: {
email: {
to: ['[email protected]'],
from: '[email protected]',
senderName: 'Novu Team',
text: 'text version of email using overrides',
replyTo: '[email protected]',
cc: ['[email protected]'],
bcc: ['[email protected]'],
},
},
});
</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="workflowId",
to={"subscriber_id": "subscriberId"},
overrides={
"email": {
"to": ["[email protected]"],
"from": "[email protected]",
"senderName": "Novu Team",
"text": "text version of email using overrides",
"replyTo": "[email protected]",
"cc": ["[email protected]"],
"bcc": ["[email protected]"],
},
},
))
novugo "github.com/novuhq/novu-go"
"github.com/novuhq/novu-go/models/components"
)
s := novugo.New(novugo.WithSecurity(os.Getenv("NOVU_SECRET_KEY")))
_, err := s.Trigger(context.Background(), components.TriggerEventRequestDto{ WorkflowID: "workflowId", To: components.CreateToSubscriberPayloadDto(components.SubscriberPayloadDto{ SubscriberID: "subscriberId", }), Overrides: map[string]map[string]any{ "email": { "to": []string{"[email protected]"}, "from": "[email protected]", "senderName": "Novu Team", "text": "text version of email using overrides", "replyTo": "[email protected]", "cc": []string{"[email protected]"}, "bcc": []string{"[email protected]"}, }, }, }, nil)
</Tab>
<Tab title="PHP">
```php
use novu;
use novu\Models\Components;
$sdk = novu\Novu::builder()
->setSecurity('<NOVU_SECRET_KEY>')
->build();
$sdk->trigger(
triggerEventRequestDto: new Components\TriggerEventRequestDto(
workflowId: 'workflowId',
to: new Components\SubscriberPayloadDto(subscriberId: 'subscriberId'),
overrides: [
'email' => [
'to' => ['[email protected]'],
'from' => '[email protected]',
'senderName' => 'Novu Team',
'text' => 'text version of email using overrides',
'replyTo' => '[email protected]',
'cc' => ['[email protected]'],
'bcc' => ['[email protected]'],
],
],
),
);
var sdk = new NovuSDK(secretKey: "<NOVU_SECRET_KEY>");
await sdk.TriggerAsync(triggerEventRequestDto: new TriggerEventRequestDto() { WorkflowId = "workflowId", To = To.CreateSubscriberPayloadDto(new SubscriberPayloadDto() { SubscriberId = "subscriberId", }), Overrides = new Overrides() { Email = new Dictionary<string, object>() { { "to", new List<string>() { "[email protected]" } }, { "from", "[email protected]" }, { "senderName", "Novu Team" }, { "text", "text version of email using overrides" }, { "replyTo", "[email protected]" }, { "cc", new List<string>() { "[email protected]" } }, { "bcc", new List<string>() { "[email protected]" } }, }, }, });
</Tab>
<Tab title="Java">
```java
import co.novu.Novu;
import co.novu.models.components.*;
import java.util.List;
import java.util.Map;
Novu novu = Novu.builder()
.secretKey("<NOVU_SECRET_KEY>")
.build();
novu.trigger()
.body(TriggerEventRequestDto.builder()
.workflowId("workflowId")
.to(To2.of(SubscriberPayloadDto.builder()
.subscriberId("subscriberId")
.build()))
.overrides(TriggerEventRequestDtoOverrides.builder()
.email(Map.of(
"to", List.of("[email protected]"),
"from", "[email protected]",
"senderName", "Novu Team",
"text", "text version of email using overrides",
"replyTo", "[email protected]",
"cc", List.of("[email protected]"),
"bcc", List.of("[email protected]")))
.build())
.build())
.call();
to addresses with the subscriber email.replaceToRecipient: true to send only to override recipients. The subscriber email is omitted from to. to is replaced with the from address.replaceToRecipient is true, include at least one of to, cc, or bcc.Empty to with cc/bcc (Custom SMTP only)
to: [] with cc or bcc is supported only on Custom SMTP (Nodemailer).to to undisclosed-recipients:; so the message sends without exposing addresses in the To field.By default, Novu uses your primary email provider. However, if you want to bypass this and force a specific, active integration for a trigger, use the integrationIdentifier.
This is useful if you have multiple active integrations for different purposes. For example, you might have one integration for transactional emails and one for marketing emails. You can find the integrationIdentifier for each provider in provider page in the Integration Store.
const novu = new Novu({ secretKey: "<NOVU_SECRET_KEY>", // Required if using EU region // serverURL: "https://eu.api.novu.co", });
await novu.trigger({ workflowId: "workflowId", to: { subscriberId: "subscriberId", }, overrides: { email: { integrationIdentifier: "brevo-abcdef" }, }, });
</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="workflowId",
to={"subscriber_id": "subscriberId"},
overrides={
"email": {
"integrationIdentifier": "brevo-abcdef",
},
},
))
novugo "github.com/novuhq/novu-go"
"github.com/novuhq/novu-go/models/components"
)
s := novugo.New(novugo.WithSecurity(os.Getenv("NOVU_SECRET_KEY")))
_, err := s.Trigger(context.Background(), components.TriggerEventRequestDto{ WorkflowID: "workflowId", To: components.CreateToSubscriberPayloadDto(components.SubscriberPayloadDto{ SubscriberID: "subscriberId", }), Overrides: map[string]map[string]any{ "email": { "integrationIdentifier": "brevo-abcdef", }, }, }, nil)
</Tab>
<Tab title="PHP">
```php
use novu;
use novu\Models\Components;
$sdk = novu\Novu::builder()
->setSecurity('<NOVU_SECRET_KEY>')
->build();
$sdk->trigger(
triggerEventRequestDto: new Components\TriggerEventRequestDto(
workflowId: 'workflowId',
to: new Components\SubscriberPayloadDto(subscriberId: 'subscriberId'),
overrides: [
'email' => [
'integrationIdentifier' => 'brevo-abcdef',
],
],
),
);
var sdk = new NovuSDK(secretKey: "<NOVU_SECRET_KEY>");
await sdk.TriggerAsync(triggerEventRequestDto: new TriggerEventRequestDto() { WorkflowId = "workflowId", To = To.CreateSubscriberPayloadDto(new SubscriberPayloadDto() { SubscriberId = "subscriberId", }), Overrides = new Overrides() { Email = new Dictionary<string, object>() { { "integrationIdentifier", "brevo-abcdef" }, }, }, });
</Tab>
<Tab title="Java">
```java
import co.novu.Novu;
import co.novu.models.components.*;
import java.util.Map;
Novu novu = Novu.builder()
.secretKey("<NOVU_SECRET_KEY>")
.build();
novu.trigger()
.body(TriggerEventRequestDto.builder()
.workflowId("workflowId")
.to(To2.of(SubscriberPayloadDto.builder()
.subscriberId("subscriberId")
.build()))
.overrides(TriggerEventRequestDtoOverrides.builder()
.email(Map.of("integrationIdentifier", "brevo-abcdef"))
.build())
.build())
.call();
Each Novu environment can have multiple layouts. Each email step in the workflow can have a layout assigned to it. Overriding the email layout allows you to dynamically override layout settings at trigger time, providing flexible layout management per workflow/channel or per step execution.
layoutId values and its behavior:
| Value | Behavior |
|---|---|
| "layout-identifier" | Uses layout with this identifier |
| "507f1f77bcf86cd799439011" | Uses layout with this MongoDB ObjectId |
| null | Explicitly no layout - renders email without any layout |
| undefined | Default behavior - uses step's configured layout or environment default |
| Not specified | Same as undefined |
Precedence rules:
const novu = new Novu({ secretKey: "<NOVU_SECRET_KEY>", // Required if using EU region // serverURL: "https://eu.api.novu.co", });
await novu.trigger({ workflowId: "workflowId", to: { subscriberId: "subscriberId", }, payload: {}, overrides: { // Channel-level layouts channels: { email: { layoutId: 'black-friday-layout', }, }, steps: { 'welcome-email': { // This email step uses a specific welcome layout layoutId: 'welcome-v2' }, 'promotional-email': { // This email uses the channel-level layout (black-friday-layout) // No layoutId specified = inherits from channel level }, 'transactional-receipt': { // This email explicitly uses no layout (plain email) layoutId: null } } } });
</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="workflowId",
to={"subscriber_id": "subscriberId"},
payload={},
overrides={
"channels": {
"email": {
"layoutId": "black-friday-layout",
},
},
"steps": {
"welcome-email": {
"layoutId": "welcome-v2",
},
"promotional-email": {},
"transactional-receipt": {
"layoutId": None,
},
},
},
))
novugo "github.com/novuhq/novu-go"
"github.com/novuhq/novu-go/models/components"
)
s := novugo.New(novugo.WithSecurity(os.Getenv("NOVU_SECRET_KEY")))
_, err := s.Trigger(context.Background(), components.TriggerEventRequestDto{ WorkflowID: "workflowId", To: components.CreateToSubscriberPayloadDto(components.SubscriberPayloadDto{ SubscriberID: "subscriberId", }), Payload: map[string]any{}, Overrides: map[string]map[string]any{ "channels": { "email": map[string]any{ "layoutId": "black-friday-layout", }, }, "steps": map[string]any{ "welcome-email": map[string]any{ "layoutId": "welcome-v2", }, "promotional-email": map[string]any{}, "transactional-receipt": map[string]any{ "layoutId": nil, }, }, }, }, nil)
</Tab>
<Tab title="PHP">
```php
use novu;
use novu\Models\Components;
$sdk = novu\Novu::builder()
->setSecurity('<NOVU_SECRET_KEY>')
->build();
$sdk->trigger(
triggerEventRequestDto: new Components\TriggerEventRequestDto(
workflowId: 'workflowId',
to: new Components\SubscriberPayloadDto(subscriberId: 'subscriberId'),
payload: [],
overrides: [
'channels' => [
'email' => [
'layoutId' => 'black-friday-layout',
],
],
'steps' => [
'welcome-email' => [
'layoutId' => 'welcome-v2',
],
'promotional-email' => [],
'transactional-receipt' => [
'layoutId' => null,
],
],
],
),
);
var sdk = new NovuSDK(secretKey: "<NOVU_SECRET_KEY>");
await sdk.TriggerAsync(triggerEventRequestDto: new TriggerEventRequestDto() { WorkflowId = "workflowId", To = To.CreateSubscriberPayloadDto(new SubscriberPayloadDto() { SubscriberId = "subscriberId", }), Payload = new Dictionary<string, object>(), Overrides = new Overrides() { Channels = new Channels() { Email = new EmailChannelOverrides() { LayoutId = "black-friday-layout", }, }, Steps = new Dictionary<string, StepsOverrides>() { { "welcome-email", new StepsOverrides() { LayoutId = "welcome-v2" } }, { "promotional-email", new StepsOverrides() }, { "transactional-receipt", new StepsOverrides() { LayoutId = null } }, }, }, });
</Tab>
<Tab title="Java">
```java
import co.novu.Novu;
import co.novu.models.components.*;
import java.util.Map;
Novu novu = Novu.builder()
.secretKey("<NOVU_SECRET_KEY>")
.build();
novu.trigger()
.body(TriggerEventRequestDto.builder()
.workflowId("workflowId")
.to(To2.of(SubscriberPayloadDto.builder()
.subscriberId("subscriberId")
.build()))
.payload(Map.of())
.overrides(TriggerEventRequestDtoOverrides.builder()
.channels(Channels.builder()
.email(EmailChannelOverrides.builder()
.layoutId("black-friday-layout")
.build())
.build())
.steps(Map.of(
"welcome-email", StepsOverrides.builder()
.layoutId("welcome-v2")
.build(),
"promotional-email", StepsOverrides.builder().build(),
"transactional-receipt", StepsOverrides.builder()
.layoutId(null)
.build()))
.build())
.build())
.call();
You can send provider specific extra fields by passing a _passthrough object in the overrides object. This lets you send extra fields to the provider SDK. Novu internally uses provider's official SDK to send the email. Each provider has its own supported extra fields. Those extra fields are not validated by Novu and are passed directly to the provider SDK. There are three type of fields that you can send: body, headers, and query. Below is an example of sending tags supported by Resend provider.
const novu = new Novu({ secretKey: "<NOVU_SECRET_KEY>", // Required if using EU region // serverURL: "https://eu.api.novu.co", });
await novu.trigger({ workflowId: "workflowId", to: { subscriberId: "subscriberId", }, payload: {}, overrides: { providers: { resend: { _passthrough: { body: { tags: [ { name: "category", value: "confirm_email" } ] }, headers: { "X-Custom-Header": "custom-header-value" }, query: { "queryParam": "queryValue" } } } } } });
</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="workflowId",
to={"subscriber_id": "subscriberId"},
payload={},
overrides={
"providers": {
"resend": {
"_passthrough": {
"body": {
"tags": [
{
"name": "category",
"value": "confirm_email",
},
],
},
"headers": {
"X-Custom-Header": "custom-header-value",
},
"query": {
"queryParam": "queryValue",
},
},
},
},
},
))
novugo "github.com/novuhq/novu-go"
"github.com/novuhq/novu-go/models/components"
)
s := novugo.New(novugo.WithSecurity(os.Getenv("NOVU_SECRET_KEY")))
_, err := s.Trigger(context.Background(), components.TriggerEventRequestDto{ WorkflowID: "workflowId", To: components.CreateToSubscriberPayloadDto(components.SubscriberPayloadDto{ SubscriberID: "subscriberId", }), Payload: map[string]any{}, Overrides: map[string]map[string]any{ "providers": { "resend": map[string]any{ "_passthrough": map[string]any{ "body": map[string]any{ "tags": []map[string]any{ { "name": "category", "value": "confirm_email", }, }, }, "headers": map[string]any{ "X-Custom-Header": "custom-header-value", }, "query": map[string]any{ "queryParam": "queryValue", }, }, }, }, }, }, nil)
</Tab>
<Tab title="PHP">
```php
use novu;
use novu\Models\Components;
$sdk = novu\Novu::builder()
->setSecurity('<NOVU_SECRET_KEY>')
->build();
$sdk->trigger(
triggerEventRequestDto: new Components\TriggerEventRequestDto(
workflowId: 'workflowId',
to: new Components\SubscriberPayloadDto(subscriberId: 'subscriberId'),
payload: [],
overrides: [
'providers' => [
'resend' => [
'_passthrough' => [
'body' => [
'tags' => [
[
'name' => 'category',
'value' => 'confirm_email',
],
],
],
'headers' => [
'X-Custom-Header' => 'custom-header-value',
],
'query' => [
'queryParam' => 'queryValue',
],
],
],
],
],
),
);
var sdk = new NovuSDK(secretKey: "<NOVU_SECRET_KEY>");
await sdk.TriggerAsync(triggerEventRequestDto: new TriggerEventRequestDto() { WorkflowId = "workflowId", To = To.CreateSubscriberPayloadDto(new SubscriberPayloadDto() { SubscriberId = "subscriberId", }), Payload = new Dictionary<string, object>(), Overrides = new Overrides() { Providers = new Dictionary<string, Dictionary<string, object>>() { { "resend", new Dictionary<string, object>() { { "_passthrough", new Dictionary<string, object>() { { "body", new Dictionary<string, object>() { { "tags", new List<Dictionary<string, object>>() { new Dictionary<string, object>() { { "name", "category" }, { "value", "confirm_email" }, }, } }, } }, { "headers", new Dictionary<string, object>() { { "X-Custom-Header", "custom-header-value" }, } }, { "query", new Dictionary<string, object>() { { "queryParam", "queryValue" }, } }, } }, } }, }, }, });
</Tab>
<Tab title="Java">
```java
import co.novu.Novu;
import co.novu.models.components.*;
import java.util.List;
import java.util.Map;
Novu novu = Novu.builder()
.secretKey("<NOVU_SECRET_KEY>")
.build();
novu.trigger()
.body(TriggerEventRequestDto.builder()
.workflowId("workflowId")
.to(To2.of(SubscriberPayloadDto.builder()
.subscriberId("subscriberId")
.build()))
.payload(Map.of())
.overrides(TriggerEventRequestDtoOverrides.builder()
.providers(Map.of(
"resend", Map.of(
"_passthrough", Map.of(
"body", Map.of(
"tags", List.of(
Map.of(
"name", "category",
"value", "confirm_email"))),
"headers", Map.of(
"X-Custom-Header", "custom-header-value"),
"query", Map.of(
"queryParam", "queryValue")))))
.build())
.build())
.call();
By default, Novu does not add any unsubscribe links to the email. Few providers add unsubscribe links to the email by default. If you are looking to add custom unsubscribe links to the email, follow below steps:
{{payload.unsubscribeEmail}} field in the email editor.false.unsubscribeEmail variable in the payload while triggering the workflow.Novu supports inbound email replies — not only sending mail through your email provider. The replyTo override above sets where replies are addressed when sending; to process replies, choose one of these paths:
| Goal | Approach |
|---|---|
| Two-way agent conversation on email | Connect email to an agent via ACI |
| Forward inbound mail to your backend | Inbound Email with a Webhook route → email.received |
| User replies to a workflow email open an agent thread | Workflow to conversation handoff |
Here are the email providers that are currently supported by Novu. Select any provider to see its detailed setup guide.
<Columns cols={2}> <Card title="SendGrid" href="/platform/integrations/email/sendgrid"> Learn how to use the SendGrid provider to send emails using Novu.</Card> <Card title="Amazon SES" href="/platform/integrations/email/amazon-ses"> Learn how to use the Amazon SES provider to send emails using Novu.</Card> <Card title="Postmark" href="/platform/integrations/email/postmark"> Learn how to use the Postmark provider to send emails using Novu.</Card> <Card title="Resend" href="/platform/integrations/email/resend"> Learn how to use the Resend provider to send emails using Novu.</Card> <Card title="Brevo" href="/platform/integrations/email/brevo"> Learn how to use the Brevo provider to send emails using Novu.</Card> <Card title="Mailgun" href="/platform/integrations/email/mailgun"> Learn how to use the Mailgun provider to send emails using Novu.</Card> <Card title="Mailjet" href="/platform/integrations/email/mailjet"> Learn how to use the Mailjet provider to send emails using Novu.</Card> <Card title="Braze" href="/platform/integrations/email/braze"> Learn how to use the Braze provider to send emails using Novu.</Card> <Card title="Infobip" href="/platform/integrations/email/infobip"> Learn how to use the Infobip provider to send emails using Novu.</Card> <Card title="MailerSend" href="/platform/integrations/email/mailersend"> Learn how to use the MailerSend provider to send emails using Novu.</Card> <Card title="Mailtrap" href="/platform/integrations/email/mailtrap"> Learn how to use the Mailtrap provider to send emails using Novu.</Card> <Card title="Mandrill" href="/platform/integrations/email/mandrill"> Learn how to use the Mandrill provider to send emails using Novu.</Card> <Card title="Maqsam" href="/platform/integrations/email/maqsam"> Learn how to use the Maqsam provider to send emails using Novu.</Card> <Card title="Netcore" href="/platform/integrations/email/netcore"> Learn how to use the Netcore provider to send emails using Novu.</Card> <Card title="Outlook 365" href="/platform/integrations/email/outlook365"> Learn how to use the Outlook 365 provider to send emails using Novu.</Card> <Card title="Plunk" href="/platform/integrations/email/plunk"> Learn how to use the Plunk provider to send emails using Novu.</Card> <Card title="Sparkpost" href="/platform/integrations/email/sparkpost"> Learn how to use the Sparkpost provider to send emails using Novu.</Card> <Card title="Email Webhook" href="/platform/integrations/email/webhook"> Learn how to use the Email Webhook provider to send emails using Novu.</Card> <Card title="Custom SMTP" href="/platform/integrations/email/custom-smtp"> Learn how to use a Custom SMTP provider to send emails using Novu.</Card> </Columns>