diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 00000000..863960a4 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,15 @@ +{ + "extends": [ + "eslint:recommended", + "plugin:mdx/recommended" + ], + // optional, if you want to lint code blocks at the same time + "settings": { + "mdx/code-blocks": false + }, + "rules": { + "no-undef": "off", + "no-unused-expressions": "off", + "no-unused-vars": "off" + } +} diff --git a/.github/workflows/invalidate-echo-snippet.yml b/.github/workflows/invalidate-echo-snippet.yml index ac9a0c62..58239965 100644 --- a/.github/workflows/invalidate-echo-snippet.yml +++ b/.github/workflows/invalidate-echo-snippet.yml @@ -7,13 +7,12 @@ on: - next - main paths: - - 'echo-terminal.js' + - "echo-terminal.js" jobs: - build: - runs-on: ubuntu-latest - - steps: - - run: "curl -X GET https://purge.jsdelivr.net/gh/novuhq/docs/echo-terminal.min.js" - - run: "curl -X GET https://purge.jsdelivr.net/gh/novuhq/docs/echo-terminal.js" - \ No newline at end of file + build: + runs-on: ubuntu-latest + + steps: + - run: "curl -X GET https://purge.jsdelivr.net/gh/novuhq/docs/echo-terminal.min.js" + - run: "curl -X GET https://purge.jsdelivr.net/gh/novuhq/docs/echo-terminal.js" diff --git a/.prettierignore b/.prettierignore index d926176d..164f3a47 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,4 +1,3 @@ # Ignore all HTML files: -**/*.mdx script.js hubspot.js \ No newline at end of file diff --git a/.speakeasy/workflow.yaml b/.speakeasy/workflow.yaml index 2d133b94..c085d717 100644 --- a/.speakeasy/workflow.yaml +++ b/.speakeasy/workflow.yaml @@ -1,12 +1,12 @@ workflowVersion: 1.0.0 speakeasyVersion: latest sources: - openapi-with-code-samples: - inputs: - - location: registry.speakeasyapi.dev/novu/novu/json-development:main - overlays: - - location: registry.speakeasyapi.dev/novu/novu/code-samples-typescript:main - output: ./openapi.json - registry: - location: registry.speakeasyapi.dev/novu/novu/openapi-with-code-samples + openapi-with-code-samples: + inputs: + - location: registry.speakeasyapi.dev/novu/novu/json-development:main + overlays: + - location: registry.speakeasyapi.dev/novu/novu/code-samples-typescript:main + output: ./openapi.json + registry: + location: registry.speakeasyapi.dev/novu/novu/openapi-with-code-samples targets: {} diff --git a/README.md b/README.md index 55020121..fcc373d2 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ First, install the [Mintlify CLI](https://www.npmjs.com/package/mintlify) locall npm i -g mintlify ``` -__Note__: Please install Node.js (version 18 or higher) before proceeding. +**Note**: Please install Node.js (version 18 or higher) before proceeding. Run the following command at the root of the documentation (where mint.json is) to preview the documentation changes diff --git a/activity-feed/introduction.mdx b/activity-feed/introduction.mdx deleted file mode 100644 index b4030ead..00000000 --- a/activity-feed/introduction.mdx +++ /dev/null @@ -1,74 +0,0 @@ ---- -title: 'Activity Feed' -description: 'Monitor all your activity from one place' -icon: 'code' ---- - -## Introduction -Novu provides a notification activity feed that monitors every outgoing message associated with its relevant metadata. This can be used to monitor activity and discover potential issues with a specific provider or channel type. - - - -## Viewing a specific subscriber's activity - -To use a subscriber's activity feed you can filter the feed by using the subscriberId or email. This will show only the relevant data for this particular subscriber. - -To do this, just enter the `subscriberId` or the 'email' of the subscriber you want to get info of and then click the 'search' button on the right-hand side as shown in the image below: - - - -## Filter by workflow or channel - -The activity feed allows you to filter messages based on the associated workflow or channel. This is helpful when you want to assess or debug a particular workflow and its behaviour. - -Filtering by workflow or channel is quite simple. Just go to the Workflow dropdown (if you want to filter by workflow) or the channel tab (if you want to filter by channel) and select the corresponding workflow or channel and click the 'search' button on the right-hand side as shown in the image below: - - - -## Troubleshooting with the activity feed - -The activity feed also shows if a transaction is failing. You can see why a transaction is failing and rectify it. For example, take a look at the following image: - -You can see that an SMS notification has failed to get delivered. Clicking on it opens the execution details and the reason why that transaction failed - - -This can help troubleshoot the problem and comes in handy at the time of distress. - -## Digest info in the activity feed - -If you’re using our digest notifications functionality, you’ll be able to see all the digest-related information in the activity feed. - -Some of the information related to digest present in the activity feed is: - -- Digest step delay -- Digesting start time -- Digest triggered events details -- More digest execution details - - - -From here, you can see the details of ‘digest triggered events’ to see exactly which events were digested and the content of the notification you sent, as shown below: - - - -## Trigger Event Lifecycle - -### **Trigger request** - -The request contains details such as the identifier for the template, the list of subscribers who will receive the notification, the payload of the notification, and any overrides that need to be applied. - -### Trigger endpoint - -After the request is sent to the `/event/trigger` endpoint, the initial step involves mapping and validating the subscribers for that event. Following that, we validate the template and steps based on the active status or draft template flag. - -Once the validation process is complete, the attachments are uploaded to the storage service. - -Next, the trigger event with mapped subscribers and attachment links is appended to the **trigger event queue**. By queuing the events, we can achieve faster response times. - -### **Trigger event processing** - -When the event is picked up by the **trigger queue worker** we process it. For every subscriber listed in the trigger event, we create a notification entity and the corresponding jobs based on the notification template steps. The notification contains data related to the organization, template, subscriber, and event payload. It is updated with a channels field that is generated from the steps. - -### **Jobs** - -Every job is created according to the steps outlined in the template. If the template contains a digest step, the **filtering logic** is applied to produce the appropriate jobs array depending on the type of digesting: **regular** or **backoff**. \ No newline at end of file diff --git a/activity-feed/trigger-event-lifecycle.mdx b/activity-feed/trigger-event-lifecycle.mdx deleted file mode 100644 index 4675a2dc..00000000 --- a/activity-feed/trigger-event-lifecycle.mdx +++ /dev/null @@ -1,71 +0,0 @@ ---- -title: 'Trigger Event Lifecycle' -description: 'Managing Trigger Events from Request to Processing' -icon: 'arrows-spin' ---- - -## Trigger Request - -A trigger request is the initial step in handling a trigger event. It contains crucial details such as the template identifier, a list of subscribers who will receive the notification, the payload of the notification, and any overrides that need to be applied. - -## Trigger Endpoint - -Upon sending the request to the `/event/trigger` endpoint, a series of essential steps are initiated: - -1. **Subscriber Mapping and Validation:** The first step involves mapping and validating the subscribers for the specified event. This ensures that notifications are sent to the correct recipients. - -2. **Template Validation:** Following subscriber validation, the template associated with the event is validated. This validation process considers factors such as the active status or draft template flag to determine if it meets the necessary criteria for processing. - -3. **Attachment Upload:** Once the validation process is successfully completed, any attachments associated with the event are uploaded to the designated storage service. - -4. **Event Queuing:** The trigger event, now enriched with mapped subscribers and attachment links, is appended to the **trigger event queue**. This queuing mechanism optimizes response times, ensuring efficient event processing. - -## Trigger Event Processing - -When an event is picked up by the **trigger queue worker**, the processing phase begins. Here's what happens: - -- **Notification Entity Creation:** For each subscriber listed in the trigger event, a corresponding notification entity is created. This entity contains essential data related to the organization, template, subscriber, and event payload. - -- **Job Creation:** Based on the notification template's defined steps, jobs are generated. These jobs are responsible for carrying out specific tasks related to the event notification. Additionally, the notification entity is updated with a "channels" field generated from these steps, indicating the communication channels through which notifications will be sent. - -## Jobs - -Jobs play a pivotal role in the trigger event lifecycle. They are created based on the steps outlined in the notification template. Depending on the presence of a digest step, the following logic is applied: - -- **Digest Step:** If the template includes a digest step, filtering logic is applied to produce an appropriate array of jobs. This logic differentiates between two types of digesting: **regular** and **backoff**, each serving distinct purposes in event processing. - -## Job Statuses - - -| Status | Description | -|-------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------| -| **PENDING** | This status is assigned to a job before it is added to the worker queue. It indicates that the job is waiting to be processed. | -| **QUEUED** | After the initial validation and just before adding a job to the worker queue, it is set to `QUEUED`. This status signifies that the job is ready for processing but is awaiting its turn in the queue. | -| **RUNNING** | When a job is picked up by a worker from the queue, its status is changed to `RUNNING`. This indicates that the job is currently being processed by a worker. | -| **COMPLETED** | Once a job has been successfully executed and processed, its status is changed to `COMPLETED`. This signifies that the job has been successfully completed. | -| **FAILED** | If a job encounters an issue during processing or execution, its status is changed to `FAILED`. This indicates that the job has not been successfully completed, and there may have been errors or problems during processing. | -| **DELAYED** | The `DELAYED` status is applied to specific types of jobs, such as `digest` or `delay` jobs, to indicate that they are delayed and not immediately processed. For `digest` jobs, it means that the digesting process is running or scheduled for a later time. For `delay` jobs, it signifies that the job is set to be executed at a specified delay time. | -| **CANCELED** | When a job is canceled for any reason, its status is set to `CANCELED`. This indicates that the job will not be processed further and is effectively removed from the processing queue. | -| **MERGED** | The `MERGED` status is assigned to events that are part of a `digest`. It indicates that an event will be merged into the digesting event. In a digesting process, there is typically a primary or initial event that serves as the digesting event, and subsequent events are merged into it. Instead of having a separate `COMPLETED` status for these merged events, they are marked as `MERGED` to indicate their specific role in the digesting process. | -| **SKIPPED** | The `SKIPPED` status is used in the context of backoff versions of digesting. In this scenario, the first event's digesting is skipped, and the second event takes on the digesting role. The `SKIPPED` status is applied to the first event's digesting, indicating that it was intentionally skipped in the digesting process. Subsequent events may be merged into the second event's digesting process, as explained with the `MERGED` status. The `SKIPPED` status helps differentiate the skipped event from others in the digesting sequence. | - -**Example:** - - - - - `PENDING` - - - `QUEUED` - - - `RUNNING` - - - `RUNNING` - - - `COMPLETED` - - diff --git a/additional-resources/data-migrations.mdx b/additional-resources/data-migrations.mdx index 33e2df25..a6a053e9 100644 --- a/additional-resources/data-migrations.mdx +++ b/additional-resources/data-migrations.mdx @@ -1,7 +1,7 @@ --- -title: 'Data Migrations' -description: 'Learn how to update your database data through migrations.' -icon: 'database' +title: "Data Migrations" +description: "Learn how to update your database data through migrations." +icon: "database" --- On occasion, Novu may introduce features that require changes to the database schema or data. @@ -25,15 +25,15 @@ Some features may have multiple migrations, in which case you will need to run e Below you will find a list of migrations introduced in previous versions of Novu, alongside the migration path to use in the script above. -| Version | Feature | Migration Path(s) | -| --- | --- | --- | -| [v0.23](https://github.com/novuhq/novu/releases/tag/v0.23.0) | API keys encryption | `./encrypt-api-keys/encrypt-api-keys-migration.ts` | -| [v0.18](https://github.com/novuhq/novu/releases/tag/v0.18.0) | Multi-Provider | `./integration-scheme-update/add-primary-priority-migration.ts`
`./integration-scheme-update/add-integration-identifier-migration.ts` | -| | Integration Store | `./novu-integrations/novu-integrations.migration.ts` | -| [v0.16](https://github.com/novuhq/novu/releases/tag/v0.16.0) | In-App Integration | `./in-app-integration/in-app-integration.migration.ts` | -| | Secure Flag Fix | `./secure-to-boolean/secure-to-boolean-migration.ts` | -| [v0.15](https://github.com/novuhq/novu/releases/tag/v0.15.0) | Database TTL | `./expire-at/expire-at.migration.ts` | -| [v0.12](https://github.com/novuhq/novu/releases/tag/v0.12.0) | Organization Invite Fix | `./normalize-users-email/normalize-users-email.migration.ts` | -| [v0.9](https://github.com/novuhq/novu/releases/tag/v0.9.0) | Seen/Read Support | `./seen-read-support/seen-read-support.migration.ts` | -| [v0.8](https://github.com/novuhq/novu/releases/tag/v0.8.0) | Secure Credentials | `./fcm-credentials/fcm-credentials-migration.ts`
`./encrypt-credentials/encrypt-credentials-migration.ts` | -| [v0.4](https://github.com/novuhq/novu/releases/tag/v0.4.0) | Change Promotion | `./changes-migration.ts` | +| Version | Feature | Migration Path(s) | +| ------------------------------------------------------------ | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | +| [v0.23](https://github.com/novuhq/novu/releases/tag/v0.23.0) | API keys encryption | `./encrypt-api-keys/encrypt-api-keys-migration.ts` | +| [v0.18](https://github.com/novuhq/novu/releases/tag/v0.18.0) | Multi-Provider | `./integration-scheme-update/add-primary-priority-migration.ts`
`./integration-scheme-update/add-integration-identifier-migration.ts` | +| | Integration Store | `./novu-integrations/novu-integrations.migration.ts` | +| [v0.16](https://github.com/novuhq/novu/releases/tag/v0.16.0) | In-App Integration | `./in-app-integration/in-app-integration.migration.ts` | +| | Secure Flag Fix | `./secure-to-boolean/secure-to-boolean-migration.ts` | +| [v0.15](https://github.com/novuhq/novu/releases/tag/v0.15.0) | Database TTL | `./expire-at/expire-at.migration.ts` | +| [v0.12](https://github.com/novuhq/novu/releases/tag/v0.12.0) | Organization Invite Fix | `./normalize-users-email/normalize-users-email.migration.ts` | +| [v0.9](https://github.com/novuhq/novu/releases/tag/v0.9.0) | Seen/Read Support | `./seen-read-support/seen-read-support.migration.ts` | +| [v0.8](https://github.com/novuhq/novu/releases/tag/v0.8.0) | Secure Credentials | `./fcm-credentials/fcm-credentials-migration.ts`
`./encrypt-credentials/encrypt-credentials-migration.ts` | +| [v0.4](https://github.com/novuhq/novu/releases/tag/v0.4.0) | Change Promotion | `./changes-migration.ts` | diff --git a/additional-resources/glossary.mdx b/additional-resources/glossary.mdx index 7fb7cfc0..753d175b 100644 --- a/additional-resources/glossary.mdx +++ b/additional-resources/glossary.mdx @@ -1,7 +1,7 @@ --- -title: 'Glossary' -description: 'Definitions' -icon: 'book-open' +title: "Glossary" +description: "Definitions" +icon: "book-open" --- ## Introduction @@ -12,105 +12,127 @@ If you have any questions or need further clarification on any of the terms list ## List of key terms and definitions -## Notification +## Notification + A brief message or alert that informs users about events, updates, or some other information -## Channels + +## Channels + Novu lets you send notifications across different communication mediums, including emails, in-app messages, push notifications, SMS, and chat. Each of these five communication mediums is referred to as a notification ‘channel’. - -## Providers -Providers are responsible for handling message delivery across various channels. Novu currently supports multiple notification channels, each with its own set of providers. - - - Chat: This channel offers these providers: - - Discord - - MS Teams - - Slack - - Zulip - - Email: The Email channel features these providers: - - Sendgrid - - Amazon SES - - Sendinblue - - Resend - - SparkPost - - Postmark - - Mailjet - - Mailtrap - - Plunk - - Braze - - Mailersend - - Outlook 365 (based on a custom SMTP server) - - Mailgun - - Mandrill - - Netcore - - Infobip, and - - Custom SMTP - - SMS: The SMS channel features these providers: - - Twilio SMS - - SMS77 - - Africa’s Talking - - Infobip - - Nexmo - - Plivo - - Sendchamp - - AWS SNS - - Telnyx - - Termii - - Firetext - - Gupshup - - Clickatell - - Azure SMS - - BulkSMS - - SimpleTexting - - MessageBird - - Push Notification Providers: This channel includes these providers: - - Firebase Cloud Messaging (FCM) - - Expo Push - - Apple Push Notification Service (APNS) - - One Signal - - Pushpad - - Pusher Beams - - Push Webhook - - In-app Notification Center: Novu provides you with a set of APIs and components to create rich customized notification center experiences: - - React component - - Angular component - - Vue component - - Web component - - iFrame embed - - Custom styling - - Headless notification center + + {" "} + +## Providers Providers are responsible for handling message delivery across various +channels. Novu currently supports multiple notification channels, each with its own +set of providers. + +- Chat: This channel offers these providers: + - Discord + - MS Teams + - Slack + - Zulip +- Email: The Email channel features these providers: + - Sendgrid + - Amazon SES + - Sendinblue + - Resend + - SparkPost + - Postmark + - Mailjet + - Mailtrap + - Plunk + - Braze + - Mailersend + - Outlook 365 (based on a custom SMTP server) + - Mailgun + - Mandrill + - Netcore + - Infobip, and + - Custom SMTP +- SMS: The SMS channel features these providers: + - Twilio SMS + - SMS77 + - Africa’s Talking + - Infobip + - Nexmo + - Plivo + - Sendchamp + - AWS SNS + - Telnyx + - Termii + - Firetext + - Gupshup + - Clickatell + - Azure SMS + - BulkSMS + - SimpleTexting + - MessageBird +- Push Notification Providers: This channel includes these providers: + - Firebase Cloud Messaging (FCM) + - Expo Push + - Apple Push Notification Service (APNS) + - One Signal + - Pushpad + - Pusher Beams + - Push Webhook +- Inbox: Novu provides you with a set of APIs and components to create rich customized inbox experiences: + - React component + - Angular component + - Vue component + - Web component + - iFrame embed + - Custom styling + - Headless Inbox ## Subscribers + Subscribers are entities designated to receive the notifications you send. Each subscriber in Novu is uniquely identified by their `subscriberId`. ## Actor -An `actor` refers to a user or subscriber who initiates actions that trigger events within the system. Each actor is uniquely identified by an "actorId," also known as "subscriberId," which distinguishes them from others. -Actors hold user-related variables, such as subscriber properties and data payload, containing information like names, emails, and custom data. +An `actor` refers to a user or subscriber who initiates actions that trigger events within the system. Each actor is uniquely identified by an "actorId," also known as "subscriberId," which distinguishes them from others. -Additionally, actors can enhance notifications by allowing their avatars to be displayed, improving the context and identification of notifications. +Actors hold user-related variables, such as subscriber properties and data payload, containing information like names, emails, and custom data. +Additionally, actors can enhance notifications by allowing their avatars to be displayed, improving the context and identification of notifications. Including actors in event data enables precise tracking and personalization of actions and events in the Novu platform. ## Workflow + Workflow templates define the flow of messages sent to subscribers. + ## Topics + Topics facilitate bulk notifications to multiple subscribers simultaneously, streamlining communication. + ## Digest Engine + The digest engine aggregates multiple trigger events into a single message, ensuring efficient communication. + ## Delay Actions + Delay actions introduce time intervals between workflow steps, optimizing message delivery timing. + ## Step Filter + Step filters customize workflow by specifying notification criteria, enhancing communication efficiency. + ## Organizations + Organizations allow separation of notifications across multiple products, managed through the Novu web dashboard. - + + + {" "} + ## Environments -Novu runs all your requests in the context of an environment. By default, Novu creates two environments when your account was created, `development` and `production`. - 1. `Development environment`: The development environment is used for testing purposes and validating notification changes prior to committing them to the production environment. - 2. `Production environment`: It will be your live/production environment, you cannot make changes to this environment directly. You will first have to make the changes in the `development` environment and then promote it to `production`. This is a read-only environment. + +Novu runs all your requests in the context of an environment. By default, Novu creates two environments when your account was created, `development` and `production`. 1. `Development environment`: The development environment is used for testing purposes and validating notification changes prior to committing them to the production environment. 2. `Production environment`: It will be your live/production environment, you cannot make changes to this environment directly. You will first have to make the changes in the `development` environment and then promote it to `production`. This is a read-only environment. + ## Data associated with an environment + - Subscribers (can’t be promoted to production) - Workflows (can be promoted to production) - Messages @@ -118,8 +140,11 @@ Novu runs all your requests in the context of an environment. By default, Novu c - Connected integrations (can’t be promoted to production) - Notification feeds (can be promoted to production) - Brand-related assets and settings -## Team members + +## Team members + Members of a team have access to the Novu web dashboard. This allows you to have individuals work on and manage templates and notifications. -## Layouts -Layouts are HTML designs or structures to wrap the content of email notifications. Layouts can be manipulated and assigned to new or existing workflows within the Novu platform, allowing users to create, manage, and assign these layouts to workflows, so they can be reused to structure the appearance of notifications sent through the platform. +## Layouts + +Layouts are HTML designs or structures to wrap the content of email notifications. Layouts can be manipulated and assigned to new or existing workflows within the Novu platform, allowing users to create, manage, and assign these layouts to workflows, so they can be reused to structure the appearance of notifications sent through the platform. diff --git a/additional-resources/idempotency.mdx b/additional-resources/idempotency.mdx index 192d31ce..355fcd26 100644 --- a/additional-resources/idempotency.mdx +++ b/additional-resources/idempotency.mdx @@ -1,7 +1,7 @@ --- -title: 'Idempotent Requests' -description: 'Retry API requests fearlessly, ensuring the operation performs just once!' -icon: 'repeat-1' +title: "Idempotent Requests" +description: "Retry API requests fearlessly, ensuring the operation performs just once!" +icon: "repeat-1" --- Our platform has a seamless integration of optional [idempotent](https://en.wikipedia.org/wiki/Idempotence) requests for `POST` and `PATCH` operations. @@ -12,24 +12,24 @@ Leveraging this feature guarantees the safety and reliability of your requests, Idempotent requests are critical for workflows that require the insurance of only one successful delivery request. - This feature was introduced in version 0.22.0 and is fully operable from 0.24.0 and beyond. Currently, the Idempotency headers are not enabled on the cloud platform but are available for self-hosting. + This feature was introduced in version 0.22.0 and is fully operable from + 0.24.0 and beyond. Currently, the Idempotency headers are not enabled on the + cloud platform but are available for self-hosting. - Please be aware that idempotency might not be supported in all community SDKs. + Please be aware that idempotency might not be supported in all community SDKs. - ## How to Perform an Idempotent Request with SDKs Supporting SDKS will implement Idempotency for you as long as you have retries turned on. Not only is this seamless but it also ensures that no event request is dropped provided that your system stays up. - - Idempotency is only implemented for /events/trigger endpoint. - +Idempotency is only implemented for /events/trigger endpoint. ### Idempotent request with api + To make an idempotent request, simply include the `Idempotency-Key: ` in your request header. This key should be a unique client-generated value, boasting enough entropy to prevent collisions; we recommend a collision-resistant Unique Identifier, such as UUIDv7, CUID, or ULID, as your idempotency keys. @@ -51,7 +51,7 @@ Here are some unique pieces constraints you should be aware of especially if you 3. A ongoing processing of the initial request that hasn't yet responded will result in a `409 Conflict` error. This will include a `Retry-After` header. - Results will not saved if an API request is invalid. + Results will not saved if an API request is invalid. ## Expiring Keys @@ -60,7 +60,9 @@ All idempotency keys are ensured to be automatically removed once they are 24 ho After this period, the reused key will be treated as a new request and response. ## Practical Examples + ### Example 1 - No idempotency + ```javascript @@ -80,9 +82,11 @@ After this period, the reused key will be treated as a new request and response. }); ``` + ### Example 2 - Idempotency Active + ```javascript @@ -107,8 +111,8 @@ After this period, the reused key will be treated as a new request and response. }); ``` - + ## References diff --git a/additional-resources/posts-videos-and-articles.mdx b/additional-resources/posts-videos-and-articles.mdx index 720d6c75..cb7295fd 100644 --- a/additional-resources/posts-videos-and-articles.mdx +++ b/additional-resources/posts-videos-and-articles.mdx @@ -1,23 +1,26 @@ --- -title: 'Blog Posts, Articles & Videos' -description: 'Blog posts, articles, and videos' +title: "Blog Posts, Articles & Videos" +description: "Blog posts, articles, and videos" --- ## Blog Posts / Articles + - [🪄✨Building a blog with a liking feature using React, Hanko and Novu 🔥](https://dev.to/novu/building-a-blog-with-a-liking-feature-using-react-hanko-and-novu-1m81) - [How to add In-App notifications to any web app!](https://dev.to/novu/how-to-add-in-app-notifications-to-any-web-app-1b4n) - [How To Add In-App Notifications To Your Angular App](https://dev.to/novu/how-to-add-in-app-notifications-to-your-angular-app-2dp3) - [Creating A Hot New Food Delivery App with Novu](https://dev.to/novu/creating-a-hot-new-food-delivery-app-with-novu-2e75) ## Videos + - [How to add In-App Notifications to your Web app with Novu](https://www.youtube.com/watch?v=KD8zoUb132k) - [Subscriber (User) Management & Bulk Import Guide](https://www.youtube.com/watch?v=m5nhYEuz86I&t=92s) - [Workflow Optimization with Delay Actions](https://www.youtube.com/watch?v=kCMB-WdbzJo) ## External Resources + - [Simplify configuration management with Novu and Configu](https://configu.com/blog/video-first-steps-with-configu/) - [Vercel and Novu integration](https://vercel.com/integrations/novu) - [Novu is building open-source notification infrastructure for developers](https://venturebeat.com/business/novu-is-building-open-source-notification-infrastructure-for-developers/) - [Novu - An open-source notification infrastructure for developers](https://reactjsexample.com/-f0-9f-9a-80-open-source-notification-infrastructure-for-products/) - [Novu Tackles Notification Infrastructure Management](https://thenewstack.io/novu-tackles-notification-infrastructure-management/) - - [Novu: Omni-channel notification infrastructure for developers](https://www.decibel.vc/articles/novu-omni-channel-notification-infrastructure-for-developers) \ No newline at end of file + - [Novu: Omni-channel notification infrastructure for developers](https://www.decibel.vc/articles/novu-omni-channel-notification-infrastructure-for-developers) diff --git a/security-and-compliance/security.mdx b/additional-resources/security.mdx similarity index 99% rename from security-and-compliance/security.mdx rename to additional-resources/security.mdx index 185dd044..1b55fcca 100644 --- a/security-and-compliance/security.mdx +++ b/additional-resources/security.mdx @@ -1,6 +1,5 @@ --- title: "Security and Compliance" -sidebarTitle: "Security" description: "Common questions related to security, compliance, privacy policy and terms and conditions" icon: "shield" --- diff --git a/api-reference/changes/apply-change.mdx b/api-reference/changes/apply-change.mdx index 331d8b9c..e32c6b59 100644 --- a/api-reference/changes/apply-change.mdx +++ b/api-reference/changes/apply-change.mdx @@ -12,14 +12,15 @@ import { Novu } from '@novu/node'; const novu = new Novu(''); await novu.changes.applyOne("changeId"); -``` + +```` ```php PHP use Novu\SDK\Novu; $novu = new Novu(); $novu->applyChange($changeId, []); -``` +```` ```ruby Ruby require 'novu' @@ -75,4 +76,4 @@ public class Main { } ``` - \ No newline at end of file + diff --git a/api-reference/changes/apply-changes.mdx b/api-reference/changes/apply-changes.mdx index 199fe45a..b2c4e1ab 100644 --- a/api-reference/changes/apply-changes.mdx +++ b/api-reference/changes/apply-changes.mdx @@ -12,7 +12,8 @@ import { Novu } from '@novu/node'; const novu = new Novu(''); await novu.changes.applyMany(["changeId1", "changeId2"]); -``` + +```` ```php PHP use Novu\SDK\Novu; @@ -23,7 +24,7 @@ $novu->applyBulkChanges([ '' ] ])->toArray(); -``` +```` ```ruby Ruby require 'novu' @@ -81,4 +82,4 @@ public class Main { } ``` - \ No newline at end of file + diff --git a/api-reference/changes/get-changes-count.mdx b/api-reference/changes/get-changes-count.mdx index d94f347f..0bb0e891 100644 --- a/api-reference/changes/get-changes-count.mdx +++ b/api-reference/changes/get-changes-count.mdx @@ -12,14 +12,15 @@ import { Novu } from '@novu/node'; const novu = new Novu(''); await novu.changes.getCount(); -``` + +```` ```php PHP use Novu\SDK\Novu; $novu = new Novu(); $novu->getChangesCount()->toArray(); -``` +```` ```ruby Ruby require 'novu' @@ -57,8 +58,8 @@ public class Main { ```json Response { - data: 0 + "data": 0 } ``` - \ No newline at end of file + diff --git a/api-reference/changes/get-changes.mdx b/api-reference/changes/get-changes.mdx index fcde6127..b31aefbd 100644 --- a/api-reference/changes/get-changes.mdx +++ b/api-reference/changes/get-changes.mdx @@ -12,14 +12,15 @@ import { Novu } from '@novu/node'; const novu = new Novu(''); await novu.changes.get(); -``` + +```` ```php PHP use Novu\SDK\Novu; $novu = new Novu(); $novu->getChanges(); -``` +```` ```ruby Ruby require 'novu' @@ -70,4 +71,4 @@ public class Main { } ``` - \ No newline at end of file + diff --git a/api-reference/environments/get-api-keys.mdx b/api-reference/environments/get-api-keys.mdx index e296b702..265336fa 100644 --- a/api-reference/environments/get-api-keys.mdx +++ b/api-reference/environments/get-api-keys.mdx @@ -12,14 +12,15 @@ import { Novu } from '@novu/node'; const novu = new Novu(''); await novu.environmets.getApiKeys(); -``` + +```` ```php PHP use Novu\SDK\Novu; $novu = new Novu(); $novu->getEnvironmentsAPIKeys()->toArray(); -``` +```` ```ruby Ruby require 'novu' @@ -81,4 +82,4 @@ curl --request GET \ } ``` - \ No newline at end of file + diff --git a/api-reference/environments/get-current-environment.mdx b/api-reference/environments/get-current-environment.mdx index 5a2f37ba..af67ee80 100644 --- a/api-reference/environments/get-current-environment.mdx +++ b/api-reference/environments/get-current-environment.mdx @@ -13,7 +13,8 @@ import { Novu } from '@novu/node'; const novu = new Novu(''); await novu.environments.getCurrent(); -``` + +```` ```php PHP use Novu\SDK\Novu; @@ -21,7 +22,7 @@ use Novu\SDK\Novu; $novu = new Novu(); $novu->getCurrentEnvironment()->toArray(); -``` +```` ```ruby Ruby require 'novu' @@ -92,4 +93,4 @@ curl --request GET \ } ``` - \ No newline at end of file + diff --git a/api-reference/environments/get-environments.mdx b/api-reference/environments/get-environments.mdx index af706c4b..43d98a30 100644 --- a/api-reference/environments/get-environments.mdx +++ b/api-reference/environments/get-environments.mdx @@ -13,14 +13,15 @@ import { Novu } from '@novu/node'; const novu = new Novu(''); await novu.environments.getAll(); -``` + +```` ```php PHP use Novu\SDK\Novu; $novu = new Novu(); $novu->getEnvironments()->toArray(); -``` +```` ```ruby Ruby require 'novu' @@ -92,4 +93,4 @@ curl --request GET \ } ``` - \ No newline at end of file + diff --git a/api-reference/environments/regenerate-api-keys.mdx b/api-reference/environments/regenerate-api-keys.mdx index f1887fd5..cdec258f 100644 --- a/api-reference/environments/regenerate-api-keys.mdx +++ b/api-reference/environments/regenerate-api-keys.mdx @@ -12,14 +12,15 @@ import { Novu } from '@novu/node'; const novu = new Novu(''); await novu.environmets.regenerateApiKeys(); -``` + +```` ```php PHP use Novu\SDK\Novu; $novu = new Novu(); $key = $novu->regenerateEnvironmentsAPIKeys()->toArray(); -``` +```` ```ruby Ruby require 'novu' @@ -70,4 +71,4 @@ curl --request POST \ } ``` - \ No newline at end of file + diff --git a/api-reference/events/broadcast-event-to-all.mdx b/api-reference/events/broadcast-event-to-all.mdx index e4179d6a..090f1470 100644 --- a/api-reference/events/broadcast-event-to-all.mdx +++ b/api-reference/events/broadcast-event-to-all.mdx @@ -9,7 +9,7 @@ import ApikeyWarning from "/snippets/apikey-warning.mdx"; ```javascript Node.js -import { Novu } from '@novu/node'; +import { Novu } from "@novu/node"; const novu = new Novu(""); @@ -17,16 +17,16 @@ await novu.events.broadcast("", { payload: { customKey: "customValue", customKey1: { - nestedkey1: "nestedValue1" - } + nestedkey1: "nestedValue1", + }, }, overrides: { email: { - from: "support@novu.co" - } + from: "support@novu.co", + }, }, - tenant: "tenantIdentifier" -}) + tenant: "tenantIdentifier", +}); ``` ```php PHP @@ -147,7 +147,7 @@ public class Main { subscriberRequest.setSubscriberId(""); TriggerEventRequest triggerEventRequest = new TriggerEventRequest(); - triggerEventRequest.setName(""); + triggerEventRequest.setName(""); triggerEventRequest.setTo(subscriberRequest); triggerEventRequest.setPayload(Collections.singletonMap("", "")); @@ -163,10 +163,10 @@ using Novu; var novuConfiguration = new NovuClientConfiguration { - /** - * Defaults to https://api.novu.co/v1 + /** + * Defaults to https://api.novu.co/v1 */ - Url = "https://novu-api.my-domain.com/v1", + Url = "https://novu-api.my-domain.com/v1", ApiKey = "12345", }; @@ -196,11 +196,11 @@ var trigger = await novu.Event.TriggerBroadcastAsync(dto); ```json Response { - "data": { - "acknowledged": true, - "status": "processed", - "transactionId": "string" - } + "data": { + "acknowledged": true, + "status": "processed", + "transactionId": "string" + } } ``` diff --git a/api-reference/events/bulk-trigger-event.mdx b/api-reference/events/bulk-trigger-event.mdx index 70b2e7a9..d7d8219d 100644 --- a/api-reference/events/bulk-trigger-event.mdx +++ b/api-reference/events/bulk-trigger-event.mdx @@ -60,13 +60,13 @@ $novu = new Novu(); $novu->bulkTriggerEvent([ [ - 'name' => '', - 'to' => '', + 'name' => '', + 'to' => '', 'payload' => ['customVariables' => 'Hello'] ], [ - 'name' => '', - 'to' => '', + 'name' => '', + 'to' => '', 'payload' => ['customVariables' => 'World'] ], ])->toArray(); @@ -259,7 +259,7 @@ public class Main { triggerEventRequest1.setName(""); triggerEventRequest1.setTo(subscriberRequest); triggerEventRequest1.setPayload(Collections.singletonMap("", "")); - + TriggerEventRequest triggerEventRequest2 = new TriggerEventRequest(); triggerEventRequest2.setName(""); triggerEventRequest2.setTo(subscriberRequest); @@ -280,10 +280,10 @@ using Novu; var novuConfiguration = new NovuClientConfiguration { - /** - * Defaults to https://api.novu.co/v1 + /** + * Defaults to https://api.novu.co/v1 */ - Url = "https://novu-api.my-domain.com/v1", + Url = "https://novu-api.my-domain.com/v1", ApiKey = "12345", }; @@ -314,13 +314,13 @@ var trigger = await novu.Event.TriggerBulkAsync(payload); ```json Response { - "data": [ - { - "acknowledged": true, - "status": "processed", - "transactionId": "string" - } - ] + "data": [ + { + "acknowledged": true, + "status": "processed", + "transactionId": "string" + } + ] } ``` diff --git a/api-reference/events/cancel-triggered-event.mdx b/api-reference/events/cancel-triggered-event.mdx index 8a140039..d52c5891 100644 --- a/api-reference/events/cancel-triggered-event.mdx +++ b/api-reference/events/cancel-triggered-event.mdx @@ -9,7 +9,7 @@ import ApikeyWarning from "/snippets/apikey-warning.mdx"; ```javascript Node.js -import { Novu } from '@novu/node'; +import { Novu } from "@novu/node"; const novu = new Novu(""); @@ -102,10 +102,10 @@ using Novu; var novuConfiguration = new NovuClientConfiguration { - /** - * Defaults to https://api.novu.co/v1 + /** + * Defaults to https://api.novu.co/v1 */ - Url = "https://novu-api.my-domain.com/v1", + Url = "https://novu-api.my-domain.com/v1", ApiKey = "12345", }; @@ -119,8 +119,8 @@ var cancelled = await novu.Event.TriggerCancelAsync("triggerTransactionId"); ```json Response -{ - data: true +{ + "data": true } ``` diff --git a/api-reference/events/trigger-event.mdx b/api-reference/events/trigger-event.mdx index 7be941bc..86040662 100644 --- a/api-reference/events/trigger-event.mdx +++ b/api-reference/events/trigger-event.mdx @@ -9,26 +9,24 @@ import ApikeyWarning from "/snippets/apikey-warning.mdx"; ```javascript Node.js -import { Novu } from '@novu/node'; +import { Novu } from "@novu/node"; const novu = new Novu(""); -await novu.trigger('', - { - to: { - subscriberId: '', - email: 'john@doemail.com', - firstName: 'John', - lastName: 'Doe', +await novu.trigger("", { + to: { + subscriberId: "", + email: "john@doemail.com", + firstName: "John", + lastName: "Doe", + }, + payload: { + name: "Hello World", + organization: { + logo: "https://happycorp.com/logo.png", }, - payload: { - name: "Hello World", - organization: { - logo: 'https://happycorp.com/logo.png', - }, - }, - } -); + }, +}); ``` ```php PHP @@ -166,7 +164,7 @@ public class Main { triggerEventRequest.setName(""); triggerEventRequest.setTo(subscriberRequest); triggerEventRequest.setPayload(Collections.singletonMap("", "")); - + TriggerEventResponse response = novu.triggerEvent(triggerEventRequest); } } @@ -179,10 +177,10 @@ using Novu; var novuConfiguration = new NovuClientConfiguration { - /** - * Defaults to https://api.novu.co/v1 + /** + * Defaults to https://api.novu.co/v1 */ - Url = "https://novu-api.my-domain.com/v1", + Url = "https://novu-api.my-domain.com/v1", ApiKey = "12345", }; @@ -219,27 +217,26 @@ var trigger = await novu.Event.Trigger(payload); ```json Response { - "data": { - "acknowledged": true, - "status": "processed", - "transactionId": "string" - } + "data": { + "acknowledged": true, + "status": "processed", + "transactionId": "string" + } } ``` -The `transactionId` within Novu is a unique identifier that is used to ensure the idempotent nature of notification delivery. +The `transactionId` within Novu is a unique identifier that is used to ensure the idempotent nature of notification delivery. -When you trigger an event to send a notification, you have the option to provide a `transactionId`. If you do not provide one, Novu will generate a UUID for you. +When you trigger an event to send a notification, you have the option to provide a `transactionId`. If you do not provide one, Novu will generate a UUID for you. **This identifier is particularly useful for preventing the same notification from being sent multiple times in case the trigger event is inadvertently called more than once.** -By leveraging the `transactionId`, you can make idempotent requests, which means if the same `transactionId` is used in another request, Novu's API will recognize it and will not send the same notification again. +By leveraging the `transactionId`, you can make idempotent requests, which means if the same `transactionId` is used in another request, Novu's API will recognize it and will not send the same notification again. This upholds the principle of idempotency, ensuring that the effect of the operation is the same, no matter how many times the request is repeated with the same `transactionId`. - diff --git a/api-reference/execution-details/get-execution-details.mdx b/api-reference/execution-details/get-execution-details.mdx index 12596b75..9d98c558 100644 --- a/api-reference/execution-details/get-execution-details.mdx +++ b/api-reference/execution-details/get-execution-details.mdx @@ -14,16 +14,14 @@ curl --request GET \ ``` ```javascript Node.js -import fetch from 'node-fetch'; +import fetch from "node-fetch"; -const response = await fetch('https://api.novu.co/v1/execution-details', { - method: 'GET', - +const response = await fetch("https://api.novu.co/v1/execution-details", { + method: "GET", }); const data = await response.json(); ``` - ```php PHP use Novu\SDK\Novu; @@ -101,4 +99,4 @@ public class Main { } ``` - \ No newline at end of file + diff --git a/api-reference/feeds/create-feed.mdx b/api-reference/feeds/create-feed.mdx index 602839b2..53cb98bd 100644 --- a/api-reference/feeds/create-feed.mdx +++ b/api-reference/feeds/create-feed.mdx @@ -17,9 +17,9 @@ curl --request POST \ ``` ```javascript Node.js -import { Novu } from '@novu/node'; +import { Novu } from "@novu/node"; -const novu = new Novu(''); +const novu = new Novu(""); await novu.feeds.create(""); ``` diff --git a/api-reference/feeds/delete-feed.mdx b/api-reference/feeds/delete-feed.mdx index 2af755a7..541618b6 100644 --- a/api-reference/feeds/delete-feed.mdx +++ b/api-reference/feeds/delete-feed.mdx @@ -15,9 +15,9 @@ curl --request DELETE \ ``` ```javascript Node.js -import { Novu } from '@novu/node'; +import { Novu } from "@novu/node"; -const novu = new Novu(''); +const novu = new Novu(""); await novu.feeds.delete(""); ``` @@ -78,4 +78,4 @@ public class Main { } ``` - \ No newline at end of file + diff --git a/api-reference/feeds/get-feeds.mdx b/api-reference/feeds/get-feeds.mdx index 370919e9..15036ea3 100644 --- a/api-reference/feeds/get-feeds.mdx +++ b/api-reference/feeds/get-feeds.mdx @@ -15,9 +15,9 @@ curl --request GET \ ``` ```javascript Node.js -import { Novu } from '@novu/node'; +import { Novu } from "@novu/node"; -const novu = new Novu(''); +const novu = new Novu(""); await novu.feeds.get(); ``` @@ -77,4 +77,4 @@ public class Main { } ``` - \ No newline at end of file + diff --git a/api-reference/get-v1blueprints.mdx b/api-reference/get-v1blueprints.mdx index a4c4a0ec..536c45c6 100644 --- a/api-reference/get-v1blueprints.mdx +++ b/api-reference/get-v1blueprints.mdx @@ -4,4 +4,4 @@ openapi: get /v1/blueprints/{templateId} import ApikeyWarning from "/snippets/apikey-warning.mdx"; - \ No newline at end of file + diff --git a/api-reference/get-v1blueprintsgroup-by-category.mdx b/api-reference/get-v1blueprintsgroup-by-category.mdx index e12d0308..b2f0a932 100644 --- a/api-reference/get-v1blueprintsgroup-by-category.mdx +++ b/api-reference/get-v1blueprintsgroup-by-category.mdx @@ -4,4 +4,4 @@ openapi: get /v1/blueprints/group-by-category import ApikeyWarning from "/snippets/apikey-warning.mdx"; - \ No newline at end of file + diff --git a/api-reference/inbound-parse/validate-the-mx-record-setup-for-the-inbound-parse-functionality.mdx b/api-reference/inbound-parse/validate-the-mx-record-setup-for-the-inbound-parse-functionality.mdx index 29ed088a..a58a4472 100644 --- a/api-reference/inbound-parse/validate-the-mx-record-setup-for-the-inbound-parse-functionality.mdx +++ b/api-reference/inbound-parse/validate-the-mx-record-setup-for-the-inbound-parse-functionality.mdx @@ -9,11 +9,10 @@ import ApikeyWarning from "/snippets/apikey-warning.mdx"; ```javascript Node.js -import fetch from 'node-fetch'; +import fetch from "node-fetch"; -const response = await fetch('https://api.novu.co/v1/inbound-parse/mx/status', { - method: 'GET', - +const response = await fetch("https://api.novu.co/v1/inbound-parse/mx/status", { + method: "GET", }); const data = await response.json(); ``` @@ -63,10 +62,10 @@ public class Main { ```json Response { - "data": { + "data": { "mxRecordConfigured": true } } ``` - \ No newline at end of file + diff --git a/api-reference/integrations/create-integration.mdx b/api-reference/integrations/create-integration.mdx index a796c048..2e547059 100644 --- a/api-reference/integrations/create-integration.mdx +++ b/api-reference/integrations/create-integration.mdx @@ -33,8 +33,8 @@ curl --location 'https://api.novu.co/v1/integrations' \ ``` ```javascript Node.js -import { Novu, ChannelTypeEnum, ProvidersIdEnum } from '@novu/node'; -const novu = new Novu(''); +import { Novu, ChannelTypeEnum, ProvidersIdEnum } from "@novu/node"; +const novu = new Novu(""); const createPaylod = { name: "SendGrid", @@ -47,8 +47,8 @@ const createPaylod = { // ... other credentials as per provider }, active: true, - check: false -} + check: false, +}; await novu.integrations.create(ProvidersIdEnum.SendGrid, createPayload); ``` @@ -98,6 +98,7 @@ novu = IntegrationApi(url, api_key).create( ) ``` + ```java Java import co.novu.sdk.Novu; @@ -111,7 +112,6 @@ public class Main { } ``` - @@ -162,4 +162,4 @@ public class Main { } ``` - \ No newline at end of file + diff --git a/api-reference/integrations/delete-integration.mdx b/api-reference/integrations/delete-integration.mdx index ed76a483..fe8c8430 100644 --- a/api-reference/integrations/delete-integration.mdx +++ b/api-reference/integrations/delete-integration.mdx @@ -8,12 +8,13 @@ import ApikeyWarning from "/snippets/apikey-warning.mdx"; ```javascript Node.js -import { Novu } from '@novu/node'; +import { Novu } from '@novu/node'; const novu = new Novu(''); await novu.integrations.delete("integrationId"); -``` + +```` ```php PHP // Get integrations @@ -21,7 +22,7 @@ use Novu\SDK\Novu; $novu = new Novu(''); $novu->deleteIntegration($integrationId); -``` +```` ```ruby Ruby require 'novu' @@ -53,7 +54,6 @@ public class Main { } ``` - @@ -107,4 +107,4 @@ public class Main { } ``` - \ No newline at end of file + diff --git a/api-reference/integrations/get-active-integrations.mdx b/api-reference/integrations/get-active-integrations.mdx index 39c83dea..4dbc8f91 100644 --- a/api-reference/integrations/get-active-integrations.mdx +++ b/api-reference/integrations/get-active-integrations.mdx @@ -8,25 +8,28 @@ import ApikeyWarning from "/snippets/apikey-warning.mdx"; ```javascript Node.js -import { Novu } from '@novu/node'; +import { Novu } from '@novu/node'; const novu = new Novu(''); await novu.integrations.getActive(); -``` + +```` ```php PHP // Get integrations use Novu\SDK\Novu; $novu = new Novu(); $novu->getActiveIntegrations()->toArray(); -``` +```` + ```ruby Ruby require 'novu' client = Novu::Client.new('') client.active_integrations() ``` + ```python Python from novu.api import IntegrationApi @@ -70,4 +73,4 @@ public class Main { primary: true } ``` - \ No newline at end of file + diff --git a/api-reference/integrations/get-integrations.mdx b/api-reference/integrations/get-integrations.mdx index a3a695d1..89785c20 100644 --- a/api-reference/integrations/get-integrations.mdx +++ b/api-reference/integrations/get-integrations.mdx @@ -8,12 +8,13 @@ import ApikeyWarning from "/snippets/apikey-warning.mdx"; ```javascript Node.js -import { Novu } from '@novu/node'; +import { Novu } from '@novu/node'; const novu = new Novu(''); await novu.integrations.getAll(); -``` + +```` ```php PHP // Get integrations @@ -21,13 +22,15 @@ use Novu\SDK\Novu; $novu = new Novu(); $novu->getIntegrations()->toArray(); -``` +```` + ```ruby Ruby require 'novu' client = Novu::Client.new('') client.integrations() ``` + ```go Go package main @@ -47,6 +50,7 @@ func main() { fmt.Println(integrations) } ``` + ```python Python from novu.api import IntegrationApi @@ -69,7 +73,6 @@ public class Main { } ``` - @@ -92,4 +95,4 @@ public class Main { } ``` - \ No newline at end of file + diff --git a/api-reference/integrations/get-webhook-support-status-for-provider.mdx b/api-reference/integrations/get-webhook-support-status-for-provider.mdx index 260f8410..138b3f0c 100644 --- a/api-reference/integrations/get-webhook-support-status-for-provider.mdx +++ b/api-reference/integrations/get-webhook-support-status-for-provider.mdx @@ -8,25 +8,28 @@ import ApikeyWarning from "/snippets/apikey-warning.mdx"; ```javascript Node.js -import { Novu, ProvidersIdEnum } from '@novu/node'; +import { Novu, ProvidersIdEnum } from '@novu/node'; const novu = new Novu(''); await novu.integrations.getWebhookProviderStatus(ProvidersIdEnum.SendGrid); -``` + +```` ```php PHP // Get integrations use Novu\SDK\Novu; $novu = new Novu(); $novu->getWebhookSupportStatusForProvider($providerId)->toArray(); -``` +```` + ```ruby Ruby require 'novu' client = Novu::Client.new('') client.webhook_provider_status('') ``` + ```python Python from novu.api import IntegrationApi @@ -36,6 +39,7 @@ novu = IntegrationApi(url, api_key).status( provider_id="", # Get webhook support status for a given provider using its providerID. ) ``` + ```java Java import co.novu.sdk.Novu; @@ -49,15 +53,14 @@ public class Main { } ``` - ```json Response { - 200: "The status of the webhook for the provider requested" + "200": "The status of the webhook for the provider requested" } ``` - \ No newline at end of file + diff --git a/api-reference/integrations/set-integration-as-primary.mdx b/api-reference/integrations/set-integration-as-primary.mdx index a0f123f7..fccf9789 100644 --- a/api-reference/integrations/set-integration-as-primary.mdx +++ b/api-reference/integrations/set-integration-as-primary.mdx @@ -8,12 +8,13 @@ import ApikeyWarning from "/snippets/apikey-warning.mdx"; ```javascript Node.js -import { Novu } from '@novu/node'; +import { Novu } from '@novu/node'; const novu = new Novu(''); await novu.integrations.setIntegrationAsPrimary("integrationId") -``` + +```` ```php PHP @@ -144,4 +144,4 @@ print(response.json()) } ``` - \ No newline at end of file + diff --git a/api-reference/integrations/update-integration.mdx b/api-reference/integrations/update-integration.mdx index d295846e..24ce8f90 100644 --- a/api-reference/integrations/update-integration.mdx +++ b/api-reference/integrations/update-integration.mdx @@ -9,9 +9,9 @@ import ApikeyWarning from "/snippets/apikey-warning.mdx"; ```javascript Node.js -import { Novu, ProvidersIdEnum } from '@novu/node'; +import { Novu, ProvidersIdEnum } from "@novu/node"; -const novu = new Novu(''); +const novu = new Novu(""); const updatePayload = { name: "SendGrid", @@ -19,14 +19,17 @@ const updatePayload = { credentials: { apiKey: "SUPER_SECRET_API_KEY", from: "no-reply@novu.co", - senderName: "Novu Team" + senderName: "Novu Team", // ... other credentials as per provider }, active: true, - check: false -} + check: false, +}; -await novu.integrations.getWebhookProviderStatus(ProvidersIdEnum.SendGrid, updatePayload); +await novu.integrations.getWebhookProviderStatus( + ProvidersIdEnum.SendGrid, + updatePayload +); ``` ```php PHP @@ -80,7 +83,6 @@ public class Main { } ``` - @@ -132,4 +134,4 @@ public class Main { } ``` - \ No newline at end of file + diff --git a/api-reference/layouts/delete-layout.mdx b/api-reference/layouts/delete-layout.mdx index a36028e4..9cb6da5e 100644 --- a/api-reference/layouts/delete-layout.mdx +++ b/api-reference/layouts/delete-layout.mdx @@ -13,14 +13,15 @@ import { Novu } from '@novu/node'; const novu = new Novu(''); await novu.layouts.delete("layoutId"); -``` + +```` ```ruby Ruby require 'novu' client = Novu::Client.new('') client.delete_layout('') -``` +```` ```python Python from novu.api import LayoutApi @@ -51,10 +52,10 @@ public class Main { ```json Response { - 204: "The layout has been deleted correctly", - 404: "The layout with the layoutId provided does not exist in the database so it can not be deleted.", - 409: "Either you are trying to delete a layout that is being used or a layout that is the default in the environment." + "204": "The layout has been deleted correctly", + "404": "The layout with the layoutId provided does not exist in the database so it can not be deleted.", + "409": "Either you are trying to delete a layout that is being used or a layout that is the default in the environment." } ``` - \ No newline at end of file + diff --git a/api-reference/layouts/filter-layouts.mdx b/api-reference/layouts/filter-layouts.mdx index 1a2d5128..5e0b4035 100644 --- a/api-reference/layouts/filter-layouts.mdx +++ b/api-reference/layouts/filter-layouts.mdx @@ -13,14 +13,15 @@ import { Novu } from '@novu/node'; const novu = new Novu(''); const params = { - page: 0, // optional - pageSize: 20, // optional - sortBy: "_id" - orderBy: -1 //optional +page: 0, // optional +pageSize: 20, // optional +sortBy: "\_id" +orderBy: -1 //optional } await novu.layouts.list(params); -``` + +```` ```ruby Ruby require 'novu' @@ -32,7 +33,7 @@ client.layouts({ 'sortBy' => 'createdAt', # optional 'orderBy' => 1 # optional }) -``` +```` ```python Python from novu.api import LayoutApi @@ -64,11 +65,11 @@ public class Main { ```json Response { - data: ["data"], - page: 0, - pageSize: 0, - totalCount: 0 + "data": ["data"], + "page": 0, + "pageSize": 0, + "totalCount": 0 } ``` - \ No newline at end of file + diff --git a/api-reference/layouts/get-layout.mdx b/api-reference/layouts/get-layout.mdx index 78fd7c30..938ba5b9 100644 --- a/api-reference/layouts/get-layout.mdx +++ b/api-reference/layouts/get-layout.mdx @@ -13,7 +13,8 @@ import { Novu } from '@novu/node'; const novu = new Novu(''); await novu.layouts.get("layoutId"); -``` + +```` ```ruby Ruby require 'novu' @@ -27,7 +28,7 @@ body = { }, } client.layout('') -``` +```` ```python Python from novu.api import LayoutApi @@ -69,9 +70,7 @@ public class Main { "channel": "in_app", "content": "string", "contentType": "string", - "variables": [ - {} - ], + "variables": [{}], "isDefault": true, "isDeleted": true, "createdAt": "string", @@ -81,4 +80,4 @@ public class Main { } ``` - \ No newline at end of file + diff --git a/api-reference/layouts/layout-creation.mdx b/api-reference/layouts/layout-creation.mdx index 760e3611..2435e609 100644 --- a/api-reference/layouts/layout-creation.mdx +++ b/api-reference/layouts/layout-creation.mdx @@ -13,22 +13,23 @@ import { Novu } from '@novu/node'; const novu = new Novu(''); const payload = { - content: "

Layout Start

{{{body}}}

Layout End

", - description: "Organisation's first layout", - name: "First Layout", - identifier: "firstlayout", - variables: [ - { - type: "String", - name: "body" - required: true - defValue: "" - } - ] - isDefault: "false" +content: "

Layout Start

{{{body}}}

Layout End

", +description: "Organisation's first layout", +name: "First Layout", +identifier: "firstlayout", +variables: [ +{ +type: "String", +name: "body" +required: true +defValue: "" +} +] +isDefault: "false" } await novu.layouts.create(payload); -``` + +```` ```ruby Ruby require 'novu' @@ -42,7 +43,7 @@ payload = { 'isDefault' => true # optional } client.create_layout(payload) -``` +```` ```python Python from novu.api import LayoutApi @@ -73,10 +74,10 @@ public class Main { ```json Response { - "data": { + "data": { "_id": "Layout identifier" } } ``` - \ No newline at end of file + diff --git a/api-reference/layouts/set-default-layout.mdx b/api-reference/layouts/set-default-layout.mdx index acd04b0c..8a1ed8cd 100644 --- a/api-reference/layouts/set-default-layout.mdx +++ b/api-reference/layouts/set-default-layout.mdx @@ -13,14 +13,15 @@ import { Novu } from '@novu/node'; const novu = new Novu(''); await novu.layouts.setDefault("layoutId"); -``` + +```` ```ruby Ruby require 'novu' client = Novu::Client.new('') client.make_default_layout('') -``` +```` ```python Python from novu.api import LayoutApi @@ -57,4 +58,4 @@ public class Main { } ``` - \ No newline at end of file + diff --git a/api-reference/layouts/update-a-layout.mdx b/api-reference/layouts/update-a-layout.mdx index c2535081..049eb43c 100644 --- a/api-reference/layouts/update-a-layout.mdx +++ b/api-reference/layouts/update-a-layout.mdx @@ -13,22 +13,23 @@ import { Novu } from '@novu/node'; const novu = new Novu(''); const payloadToUpdate = { - content: "

Layout Start

{{{body}}}

Layout End

", - description: "Organisation's first layout", - name: "First Layout", - identifier: "firstlayout", - variables: [ - { - type: "String", - name: "body" - required: true - defValue: "" - } - ] - isDefault: false +content: "

Layout Start

{{{body}}}

Layout End

", +description: "Organisation's first layout", +name: "First Layout", +identifier: "firstlayout", +variables: [ +{ +type: "String", +name: "body" +required: true +defValue: "" +} +] +isDefault: false } await novu.layouts.update("layoutId", payloadToUpdate); -``` + +```` ```ruby Ruby require 'novu' @@ -42,7 +43,7 @@ payload = { 'variables' => [''], # optional } client.update_layout('', payload) -``` +```` ```python Python from novu.api import LayoutApi @@ -77,11 +78,9 @@ public class Main { "identifier": "string", "description": "string", "content": "string", - "variables": [ - {} - ], + "variables": [{}], "isDefault": true } ``` - \ No newline at end of file + diff --git a/api-reference/messages/delete-message-by-transactionid.mdx b/api-reference/messages/delete-message-by-transactionid.mdx index b64aa445..59c1621d 100644 --- a/api-reference/messages/delete-message-by-transactionid.mdx +++ b/api-reference/messages/delete-message-by-transactionid.mdx @@ -20,4 +20,4 @@ curl --request DELETE \ 204 - No Content ``` - \ No newline at end of file + diff --git a/api-reference/messages/delete-message.mdx b/api-reference/messages/delete-message.mdx index d524f6e5..7438b3fe 100644 --- a/api-reference/messages/delete-message.mdx +++ b/api-reference/messages/delete-message.mdx @@ -9,9 +9,9 @@ import ApikeyWarning from "/snippets/apikey-warning.mdx"; ```javascript Node.js -import { Novu, ChannelTypeEnum } from '@novu/node'; +import { Novu, ChannelTypeEnum } from "@novu/node"; -const novu = new Novu(''); +const novu = new Novu(""); await novu.messages.deleteById(""); ``` @@ -38,7 +38,7 @@ from novu.api import MessageApi url = "https://api.novu.co" api_key = "" novu = MessageApi(url, api_key).delete( - message_id= "The message ID to delete", + message_id= "The message ID to delete", ) ``` @@ -68,4 +68,4 @@ public class Main { } ``` - \ No newline at end of file + diff --git a/api-reference/messages/get-messages.mdx b/api-reference/messages/get-messages.mdx index 7e3a4e5c..e5787b14 100644 --- a/api-reference/messages/get-messages.mdx +++ b/api-reference/messages/get-messages.mdx @@ -86,4 +86,4 @@ public class Main { } ``` - \ No newline at end of file + diff --git a/api-reference/notification/get-notification-graph-statistics.mdx b/api-reference/notification/get-notification-graph-statistics.mdx index 87856212..6539c193 100644 --- a/api-reference/notification/get-notification-graph-statistics.mdx +++ b/api-reference/notification/get-notification-graph-statistics.mdx @@ -34,4 +34,4 @@ $novu = new Novu(''); $notificationGraphStats = $novu->getNotificationGraphStats()->toArray(); ``` - \ No newline at end of file +
diff --git a/api-reference/notification/get-notification-statistics.mdx b/api-reference/notification/get-notification-statistics.mdx index 853938c9..c05d9257 100644 --- a/api-reference/notification/get-notification-statistics.mdx +++ b/api-reference/notification/get-notification-statistics.mdx @@ -44,4 +44,4 @@ $novu = new Novu(''); $notificationStats = $novu->getNotificationStats()->toArray(); ``` -
\ No newline at end of file +
diff --git a/api-reference/organizations/create-organization.mdx b/api-reference/organizations/create-organization.mdx index 50ec615e..1aadbaf2 100644 --- a/api-reference/organizations/create-organization.mdx +++ b/api-reference/organizations/create-organization.mdx @@ -4,4 +4,4 @@ openapi: post /v1/organizations import ApikeyWarning from "/snippets/apikey-warning.mdx"; - \ No newline at end of file + diff --git a/api-reference/organizations/delete-member-from-organization.mdx b/api-reference/organizations/delete-member-from-organization.mdx index b90849ab..b9787028 100644 --- a/api-reference/organizations/delete-member-from-organization.mdx +++ b/api-reference/organizations/delete-member-from-organization.mdx @@ -5,4 +5,3 @@ openapi: delete /v1/organizations/members/{memberId} import ApikeyWarning from "/snippets/apikey-warning.mdx"; - diff --git a/api-reference/rate-limiting.mdx b/api-reference/rate-limiting.mdx index 348993f8..00c1ef03 100644 --- a/api-reference/rate-limiting.mdx +++ b/api-reference/rate-limiting.mdx @@ -3,10 +3,7 @@ title: "API Rate Limiting" description: "In this page you can learn about how rate limiting works with Novu's API" --- - - This feature is available from v0.22.0. - - +This feature is available from v0.22.0. Rate limiting is an essential functionality for establishing a robust and resilient system. It safeguards system resources from being misused by malicious actors or being monopolized by one client. @@ -17,9 +14,8 @@ A variable-cost token bucket rate limited algorithm has been added to provide th The following limits apply to each category of the Novu system. Each category has an independent bucket of rate-limit tokens to consume. Bulk requests have a rate limit cost of 100 times the base limit and consume from the same token pool. For example, the free tier would allow a combined maximum of 10 standard trigger events and 2 bulk trigger events in the same 1-second window. - -| Category | Free | Business | Endpoints | -|-----------------|----------|----------|------------------------------| -| Events | 60 rps | 600 rps | Trigger | -| Configuration | 15 rps | 150 rps | Subscribers, Topics, Tenants | -| Global | 30 rps | 300 rps | All other endpoints consume rate limiting tokens from this category. | \ No newline at end of file +| Category | Free | Business | Endpoints | +| ------------- | ------ | -------- | -------------------------------------------------------------------- | +| Events | 60 rps | 600 rps | Trigger | +| Configuration | 15 rps | 150 rps | Subscribers, Topics, Tenants | +| Global | 30 rps | 300 rps | All other endpoints consume rate limiting tokens from this category. | diff --git a/api-reference/subscribers/bulk-create-subscribers.mdx b/api-reference/subscribers/bulk-create-subscribers.mdx index e499a6e8..a733ab75 100644 --- a/api-reference/subscribers/bulk-create-subscribers.mdx +++ b/api-reference/subscribers/bulk-create-subscribers.mdx @@ -9,29 +9,30 @@ import ApikeyWarning from "/snippets/apikey-warning.mdx"; ```javascript Node.js -import { Novu } from '@novu/node'; +import { Novu } from "@novu/node"; -const novu = new Novu(''); +const novu = new Novu(""); await novu.subscribers.bulkCreate([ - { - subscriberId: 'test-subscriber-1', - email: 'test-user@sd.com', - firstName: 'subscriber-1', - lastName: 'test-1', - }, - { - subscriberId: 'test-subscriber-2', - email: 'test-user-2@sd.com', - firstName: 'subscriber-2', - lastName: 'test-2', - }, - { - subscriberId: 'test-subscriber-3', - }, + { + subscriberId: "test-subscriber-1", + email: "test-user@sd.com", + firstName: "subscriber-1", + lastName: "test-1", + }, + { + subscriberId: "test-subscriber-2", + email: "test-user-2@sd.com", + firstName: "subscriber-2", + lastName: "test-2", + }, + { + subscriberId: "test-subscriber-3", + }, ]); ``` -``` java Java + +```java Java import co.novu.common.base.Novu; import co.novu.api.common.SubscriberRequest; import co.novu.api.subscribers.responses.CreateBulkSubscriberResponse; @@ -73,22 +74,22 @@ public class Main { ```json Response { - "data": { - "updated": [], - "created": [ - { - "subscriberId": "test-subscriber-1" - }, - { - "subscriberId": "test-subscriber-2" - }, - { - "subscriberId": "test-subscriber-3" - } - ], - "failed": [] - } + "data": { + "updated": [], + "created": [ + { + "subscriberId": "test-subscriber-1" + }, + { + "subscriberId": "test-subscriber-2" + }, + { + "subscriberId": "test-subscriber-3" + } + ], + "failed": [] + } } ``` - \ No newline at end of file + diff --git a/api-reference/subscribers/create-subscriber.mdx b/api-reference/subscribers/create-subscriber.mdx index e4045d8b..e67867ac 100644 --- a/api-reference/subscribers/create-subscriber.mdx +++ b/api-reference/subscribers/create-subscriber.mdx @@ -28,7 +28,7 @@ curl --location 'https://api.novu.co/v1/subscribers' \ ``` ```javascript Node.js -import { Novu } from '@novu/node'; +import { Novu } from "@novu/node"; const novu = new Novu(""); @@ -37,12 +37,13 @@ await novu.subscribers.identify("subscriberId", { lastName: "Jain", email: "pawan.jain@domain.com", phone: "+1234567890", - avatar: "https://gravatar.com/avatar/553b157d82ac2880237566d5a644e5fe?s=400&d=robohash&r=x", + avatar: + "https://gravatar.com/avatar/553b157d82ac2880237566d5a644e5fe?s=400&d=robohash&r=x", locale: "en-US", data: { isDeveloper: true, - customKey: "customValue" - } + customKey: "customValue", + }, }); ``` @@ -128,10 +129,10 @@ using Novu; var novuConfiguration = new NovuClientConfiguration { - /** - * Defaults to https://api.novu.co/v1 + /** + * Defaults to https://api.novu.co/v1 */ - Url = "https://novu-api.my-domain.com/v1", + Url = "https://novu-api.my-domain.com/v1", ApiKey = "12345", }; @@ -172,24 +173,24 @@ var subscriber = await novu.Subscriber.CreateSubscriber(newSubscriberDto) ```json Response { - "data": { - "_organizationId": "abcd12349876wxyz1234wxyz", - "_environmentId": "zyxw4321abcd1234lmno5678", - "firstName": "Alex", - "lastName": "Benjamin", - "phone": "123456789", - "subscriberId": "abcd1234", - "email": "alex@email.com", - "avatar": "string", - "locale": "en-US", - "channels": [], - "_id": "6516ed5a9d727fe8256028ed", - "deleted": false, - "createdAt": "2023-09-29T15:29:30.667Z", - "updatedAt": "2023-09-29T15:29:30.667Z", - "__v": 0, - "id": "6516ed5a9d727fe8256028ed" - } + "data": { + "_organizationId": "abcd12349876wxyz1234wxyz", + "_environmentId": "zyxw4321abcd1234lmno5678", + "firstName": "Alex", + "lastName": "Benjamin", + "phone": "123456789", + "subscriberId": "abcd1234", + "email": "alex@email.com", + "avatar": "string", + "locale": "en-US", + "channels": [], + "_id": "6516ed5a9d727fe8256028ed", + "deleted": false, + "createdAt": "2023-09-29T15:29:30.667Z", + "updatedAt": "2023-09-29T15:29:30.667Z", + "__v": 0, + "id": "6516ed5a9d727fe8256028ed" + } } ``` diff --git a/api-reference/subscribers/delete-subscriber.mdx b/api-reference/subscribers/delete-subscriber.mdx index 9177e46f..7046bdfc 100644 --- a/api-reference/subscribers/delete-subscriber.mdx +++ b/api-reference/subscribers/delete-subscriber.mdx @@ -9,7 +9,7 @@ import ApikeyWarning from "/snippets/apikey-warning.mdx"; ```javascript Node.js -import { Novu } from '@novu/node'; +import { Novu } from "@novu/node"; const novu = new Novu(""); @@ -32,7 +32,6 @@ client = Novu::Client.new('') client.delete_subscriber('') ``` - ```python Python from novu.dto.event import InputEventDto from novu.api import EventApi @@ -44,7 +43,7 @@ api_key = "" novu = SubscriberApi(url, api_key).delete(subscriber_id) ``` -``` java Java +```java Java import co.novu.common.base.Novu; import co.novu.api.subscribers.responses.SubscriberDeleteResponse; @@ -66,10 +65,10 @@ using Novu; var novuConfiguration = new NovuClientConfiguration { - /** - * Defaults to https://api.novu.co/v1 + /** + * Defaults to https://api.novu.co/v1 */ - Url = "https://novu-api.my-domain.com/v1", + Url = "https://novu-api.my-domain.com/v1", ApiKey = "12345", }; @@ -84,11 +83,11 @@ var deleted = await novu.Subscriber.DeleteSubscriber("subscriberId"); ```json Response { - "data": { - "acknowledged": true, - "status": "deleted" - } + "data": { + "acknowledged": true, + "status": "deleted" + } } ``` - \ No newline at end of file + diff --git a/api-reference/subscribers/get-in-app-notification-feed-for-a-particular-subscriber.mdx b/api-reference/subscribers/get-in-app-notification-feed-for-a-particular-subscriber.mdx index a48c3cad..ce7f19f9 100644 --- a/api-reference/subscribers/get-in-app-notification-feed-for-a-particular-subscriber.mdx +++ b/api-reference/subscribers/get-in-app-notification-feed-for-a-particular-subscriber.mdx @@ -12,15 +12,17 @@ import co.novu.common.base.Novu; import co.novu.api.subscribers.responses.SubscriberNotificationResponse; public class Main { - public static void main(String[] args) { - String apiKey = ""; - Novu novu = new Novu(apiKey); - String subscriberId = ""; +public static void main(String[] args) { +String apiKey = ""; +Novu novu = new Novu(apiKey); +String subscriberId = ""; SubscriberNotificationResponse response = novu.getSubscriberNotificationsFeed(subscriberId); } + } -``` + +```` ```json Response @@ -192,5 +194,6 @@ public class Main { "page": "number", "pageSize": "number" } -``` - \ No newline at end of file +```` + + diff --git a/api-reference/subscribers/get-subscriber-global-preferences.mdx b/api-reference/subscribers/get-subscriber-global-preferences.mdx index 0a3531d8..b9a35dbd 100644 --- a/api-reference/subscribers/get-subscriber-global-preferences.mdx +++ b/api-reference/subscribers/get-subscriber-global-preferences.mdx @@ -4,4 +4,4 @@ openapi: get /v1/subscribers/{subscriberId}/preferences/{level} import ApikeyWarning from "/snippets/apikey-warning.mdx"; - \ No newline at end of file + diff --git a/api-reference/subscribers/get-subscriber-preferences.mdx b/api-reference/subscribers/get-subscriber-preferences.mdx index 82c41b0f..9efcdc13 100644 --- a/api-reference/subscribers/get-subscriber-preferences.mdx +++ b/api-reference/subscribers/get-subscriber-preferences.mdx @@ -12,15 +12,17 @@ import co.novu.common.base.Novu; import co.novu.api.subscribers.responses.SubscriberPreferenceResponse; public class Main { - public static void main(String[] args) { - String apiKey = ""; - Novu novu = new Novu(apiKey); - String subscriberId = ""; +public static void main(String[] args) { +String apiKey = ""; +Novu novu = new Novu(apiKey); +String subscriberId = ""; SubscriberPreferenceResponse response = novu.getSubscriberPreferences(subscriberId); } + } -``` + +```` ```json Response @@ -45,6 +47,6 @@ public class Main { } ] } -``` - +```` + diff --git a/api-reference/subscribers/get-subscriber.mdx b/api-reference/subscribers/get-subscriber.mdx index 7f03c8d4..86745d22 100644 --- a/api-reference/subscribers/get-subscriber.mdx +++ b/api-reference/subscribers/get-subscriber.mdx @@ -9,7 +9,7 @@ import ApikeyWarning from "/snippets/apikey-warning.mdx"; ```javascript Node.js -import { Novu } from '@novu/node'; +import { Novu } from "@novu/node"; const novu = new Novu(""); @@ -36,12 +36,15 @@ print(response.text) ``` ```javascript JavaScript -const options = {method: 'GET', headers: {Authorization: ''}}; - -fetch('https://api.novu.co/v1/subscribers/{subscriberId}', options) - .then(response => response.json()) - .then(response => console.log(response)) - .catch(err => console.error(err)); +const options = { + method: "GET", + headers: { Authorization: "" }, +}; + +fetch("https://api.novu.co/v1/subscribers/{subscriberId}", options) + .then((response) => response.json()) + .then((response) => console.log(response)) + .catch((err) => console.error(err)); ``` ```php PHP @@ -139,9 +142,7 @@ public class Main { "credentials": { "webhookUrl": "string", "channel": "string", - "deviceTokens": [ - "string" - ] + "deviceTokens": ["string"] }, "_integrationId": "string" } diff --git a/api-reference/subscribers/get-subscribers.mdx b/api-reference/subscribers/get-subscribers.mdx index a0c3f6d0..9a263883 100644 --- a/api-reference/subscribers/get-subscribers.mdx +++ b/api-reference/subscribers/get-subscribers.mdx @@ -12,17 +12,19 @@ import co.novu.common.base.Novu; import co.novu.api.subscribers.responses.BulkSubscriberResponse; public class Main { - public static void main(String[] args) { - String apiKey = ""; - Novu novu = new Novu(apiKey); +public static void main(String[] args) { +String apiKey = ""; +Novu novu = new Novu(apiKey); Integer page = 0; Integer limit = 10; BulkSubscriberResponse response = novu.getSubscribers(page, limit); } + } -``` + +```` @@ -66,5 +68,6 @@ public class Main { "page": "number", "pageSize": "number" } -``` - \ No newline at end of file +```` + + diff --git a/api-reference/subscribers/handle-chat-oauth.mdx b/api-reference/subscribers/handle-chat-oauth.mdx index 217d35f4..44920f19 100644 --- a/api-reference/subscribers/handle-chat-oauth.mdx +++ b/api-reference/subscribers/handle-chat-oauth.mdx @@ -4,4 +4,4 @@ openapi: get /v1/subscribers/{subscriberId}/credentials/{providerId}/oauth import ApikeyWarning from "/snippets/apikey-warning.mdx"; - \ No newline at end of file + diff --git a/api-reference/subscribers/handle-providers-oauth-redirect.mdx b/api-reference/subscribers/handle-providers-oauth-redirect.mdx index d7a1c888..62643dc4 100644 --- a/api-reference/subscribers/handle-providers-oauth-redirect.mdx +++ b/api-reference/subscribers/handle-providers-oauth-redirect.mdx @@ -4,4 +4,4 @@ openapi: get /v1/subscribers/{subscriberId}/credentials/{providerId}/oauth/callb import ApikeyWarning from "/snippets/apikey-warning.mdx"; - \ No newline at end of file + diff --git a/api-reference/subscribers/mark-a-subscriber-feed-message-as-seen.mdx b/api-reference/subscribers/mark-a-subscriber-feed-message-as-seen.mdx index bed4f570..ace7562b 100644 --- a/api-reference/subscribers/mark-a-subscriber-feed-message-as-seen.mdx +++ b/api-reference/subscribers/mark-a-subscriber-feed-message-as-seen.mdx @@ -4,4 +4,4 @@ openapi: post /v1/subscribers/{subscriberId}/messages/markAs import ApikeyWarning from "/snippets/apikey-warning.mdx"; - \ No newline at end of file + diff --git a/api-reference/subscribers/mark-message-action-as-seen.mdx b/api-reference/subscribers/mark-message-action-as-seen.mdx index 1a50770b..d60354a3 100644 --- a/api-reference/subscribers/mark-message-action-as-seen.mdx +++ b/api-reference/subscribers/mark-message-action-as-seen.mdx @@ -4,4 +4,4 @@ openapi: post /v1/subscribers/{subscriberId}/messages/{messageId}/actions/{type} import ApikeyWarning from "/snippets/apikey-warning.mdx"; - \ No newline at end of file + diff --git a/api-reference/subscribers/marks-all-the-subscriber-messages-as-read-unread-seen-or-unseen-optionally-you-can-pass-feed-id-or-array-to-mark-messages-of-a-particular-feed.mdx b/api-reference/subscribers/marks-all-the-subscriber-messages-as-read-unread-seen-or-unseen-optionally-you-can-pass-feed-id-or-array-to-mark-messages-of-a-particular-feed.mdx index 851d6e58..b3994f76 100644 --- a/api-reference/subscribers/marks-all-the-subscriber-messages-as-read-unread-seen-or-unseen-optionally-you-can-pass-feed-id-or-array-to-mark-messages-of-a-particular-feed.mdx +++ b/api-reference/subscribers/marks-all-the-subscriber-messages-as-read-unread-seen-or-unseen-optionally-you-can-pass-feed-id-or-array-to-mark-messages-of-a-particular-feed.mdx @@ -4,4 +4,4 @@ openapi: post /v1/subscribers/{subscriberId}/messages/mark-all import ApikeyWarning from "/snippets/apikey-warning.mdx"; - \ No newline at end of file + diff --git a/api-reference/subscribers/update-subscriber-credentials.mdx b/api-reference/subscribers/update-subscriber-credentials.mdx index f4e793d6..3e892e84 100644 --- a/api-reference/subscribers/update-subscriber-credentials.mdx +++ b/api-reference/subscribers/update-subscriber-credentials.mdx @@ -4,4 +4,4 @@ openapi: put /v1/subscribers/{subscriberId}/credentials import ApikeyWarning from "/snippets/apikey-warning.mdx"; - \ No newline at end of file + diff --git a/api-reference/subscribers/update-subscriber-global-preference.mdx b/api-reference/subscribers/update-subscriber-global-preference.mdx index e13bf4bc..9920b52e 100644 --- a/api-reference/subscribers/update-subscriber-global-preference.mdx +++ b/api-reference/subscribers/update-subscriber-global-preference.mdx @@ -4,4 +4,4 @@ openapi: patch /v1/subscribers/{subscriberId}/preferences import ApikeyWarning from "/snippets/apikey-warning.mdx"; - \ No newline at end of file + diff --git a/api-reference/subscribers/update-subscriber-online-status.mdx b/api-reference/subscribers/update-subscriber-online-status.mdx index c04ddb37..4a36e8e8 100644 --- a/api-reference/subscribers/update-subscriber-online-status.mdx +++ b/api-reference/subscribers/update-subscriber-online-status.mdx @@ -12,18 +12,20 @@ import co.novu.api.subscribers.responses.SingleSubscriberResponse; import co.novu.api.subscribers.requests.UpdateSubscriberOnlineStatusRequest; public class Main { - public static void main(String[] args) { - String apiKey = ""; - Novu novu = new Novu(apiKey); - String subscriberId = ""; +public static void main(String[] args) { +String apiKey = ""; +Novu novu = new Novu(apiKey); +String subscriberId = ""; UpdateSubscriberOnlineStatusRequest request = new UpdateSubscriberOnlineStatusRequest(); request.setIsOnline(true); SingleSubscriberResponse response = novu.updateSubscriberOnlineStatus(request, subscriberId); } + } -``` + +````
```json Response @@ -61,5 +63,6 @@ public class Main { "updatedAt": "string" } } -``` - \ No newline at end of file +```` + + diff --git a/api-reference/subscribers/update-subscriber-preference.mdx b/api-reference/subscribers/update-subscriber-preference.mdx index ef4f6dea..7d6b559b 100644 --- a/api-reference/subscribers/update-subscriber-preference.mdx +++ b/api-reference/subscribers/update-subscriber-preference.mdx @@ -12,11 +12,11 @@ import co.novu.api.subscribers.requests.UpdateSubscriberPreferenceRequest; import co.novu.api.subscribers.responses.SingleSubscriberPrefResponse; public class Main { - public static void main(String[] args) { - String apiKey = ""; - Novu novu = new Novu(apiKey); - String subscriberId = ""; - String templateId = ""; +public static void main(String[] args) { +String apiKey = ""; +Novu novu = new Novu(apiKey); +String subscriberId = ""; +String templateId = ""; PreferenceChannel channel = new PreferenceChannel(); channel.setType(""); @@ -28,8 +28,10 @@ public class Main { SingleSubscriberPrefResponse response = novu.updateSubscriberPreferences(request, subscriberId, templateId); } + } -``` + +```` @@ -53,5 +55,6 @@ public class Main { } } } -``` +```` + diff --git a/api-reference/subscribers/update-subscriber.mdx b/api-reference/subscribers/update-subscriber.mdx index ffd28830..2b7b1bce 100644 --- a/api-reference/subscribers/update-subscriber.mdx +++ b/api-reference/subscribers/update-subscriber.mdx @@ -12,10 +12,10 @@ import co.novu.api.subscribers.responses.SingleSubscriberResponse; import co.novu.api.subscribers.requests.UpdateSubscriberRequest; public class Main { - public static void main(String[] args) { - String apiKey = ""; - Novu novu = new Novu(apiKey); - String subscriberId = ""; +public static void main(String[] args) { +String apiKey = ""; +Novu novu = new Novu(apiKey); +String subscriberId = ""; UpdateSubscriberRequest updateSubscriberRequest = new UpdateSubscriberRequest(); updateSubscriberRequest.setEmail(""); @@ -26,8 +26,10 @@ public class Main { SingleSubscriberResponse response = novu.updateSubscriber(updateSubscriberRequest, subscriberId); } + } -``` + +```` ```json Response @@ -65,5 +67,6 @@ public class Main { "updatedAt": "string" } } -``` - \ No newline at end of file +```` + + diff --git a/api-reference/tenants/create-tenant.mdx b/api-reference/tenants/create-tenant.mdx index 68fa74b0..188a77bb 100644 --- a/api-reference/tenants/create-tenant.mdx +++ b/api-reference/tenants/create-tenant.mdx @@ -4,4 +4,4 @@ openapi: post /v1/tenants import ApikeyWarning from "/snippets/apikey-warning.mdx"; - \ No newline at end of file + diff --git a/api-reference/tenants/delete-tenant.mdx b/api-reference/tenants/delete-tenant.mdx index 4b2dbcf0..503840a3 100644 --- a/api-reference/tenants/delete-tenant.mdx +++ b/api-reference/tenants/delete-tenant.mdx @@ -4,4 +4,4 @@ openapi: delete /v1/tenants/{identifier} import ApikeyWarning from "/snippets/apikey-warning.mdx"; - \ No newline at end of file + diff --git a/api-reference/tenants/get-tenant.mdx b/api-reference/tenants/get-tenant.mdx index 703c578a..edb80a30 100644 --- a/api-reference/tenants/get-tenant.mdx +++ b/api-reference/tenants/get-tenant.mdx @@ -4,4 +4,4 @@ openapi: get /v1/tenants/{identifier} import ApikeyWarning from "/snippets/apikey-warning.mdx"; - \ No newline at end of file + diff --git a/api-reference/tenants/get-tenants.mdx b/api-reference/tenants/get-tenants.mdx index e2b285da..d580d965 100644 --- a/api-reference/tenants/get-tenants.mdx +++ b/api-reference/tenants/get-tenants.mdx @@ -4,4 +4,4 @@ openapi: get /v1/tenants import ApikeyWarning from "/snippets/apikey-warning.mdx"; - \ No newline at end of file + diff --git a/api-reference/tenants/update-tenant.mdx b/api-reference/tenants/update-tenant.mdx index 3f4f1b1e..a0b2e776 100644 --- a/api-reference/tenants/update-tenant.mdx +++ b/api-reference/tenants/update-tenant.mdx @@ -4,4 +4,4 @@ openapi: patch /v1/tenants/{identifier} import ApikeyWarning from "/snippets/apikey-warning.mdx"; - \ No newline at end of file + diff --git a/api-reference/topics/check-topic-subscriber.mdx b/api-reference/topics/check-topic-subscriber.mdx index c66c01e0..fc232046 100644 --- a/api-reference/topics/check-topic-subscriber.mdx +++ b/api-reference/topics/check-topic-subscriber.mdx @@ -4,4 +4,4 @@ openapi: get /v1/topics/{topicKey}/subscribers/{externalSubscriberId} import ApikeyWarning from "/snippets/apikey-warning.mdx"; - \ No newline at end of file + diff --git a/api-reference/topics/delete-topic.mdx b/api-reference/topics/delete-topic.mdx index 707c7b2d..a2558b9b 100644 --- a/api-reference/topics/delete-topic.mdx +++ b/api-reference/topics/delete-topic.mdx @@ -4,4 +4,4 @@ openapi: delete /v1/topics/{topicKey} import ApikeyWarning from "/snippets/apikey-warning.mdx"; - \ No newline at end of file + diff --git a/api-reference/topics/filter-topics.mdx b/api-reference/topics/filter-topics.mdx index 848c32d9..cf1ec474 100644 --- a/api-reference/topics/filter-topics.mdx +++ b/api-reference/topics/filter-topics.mdx @@ -4,4 +4,4 @@ openapi: get /v1/topics import ApikeyWarning from "/snippets/apikey-warning.mdx"; - \ No newline at end of file + diff --git a/api-reference/topics/get-topic.mdx b/api-reference/topics/get-topic.mdx index b33fb38d..b4e996c7 100644 --- a/api-reference/topics/get-topic.mdx +++ b/api-reference/topics/get-topic.mdx @@ -4,4 +4,4 @@ openapi: get /v1/topics/{topicKey} import ApikeyWarning from "/snippets/apikey-warning.mdx"; - \ No newline at end of file + diff --git a/api-reference/topics/rename-a-topic.mdx b/api-reference/topics/rename-a-topic.mdx index 17c4dfde..b4d0c787 100644 --- a/api-reference/topics/rename-a-topic.mdx +++ b/api-reference/topics/rename-a-topic.mdx @@ -4,4 +4,4 @@ openapi: patch /v1/topics/{topicKey} import ApikeyWarning from "/snippets/apikey-warning.mdx"; - \ No newline at end of file + diff --git a/api-reference/topics/subscribers-addition.mdx b/api-reference/topics/subscribers-addition.mdx index bb9fab1d..f9c8f51e 100644 --- a/api-reference/topics/subscribers-addition.mdx +++ b/api-reference/topics/subscribers-addition.mdx @@ -4,4 +4,4 @@ openapi: post /v1/topics/{topicKey}/subscribers import ApikeyWarning from "/snippets/apikey-warning.mdx"; - \ No newline at end of file + diff --git a/api-reference/topics/subscribers-removal.mdx b/api-reference/topics/subscribers-removal.mdx index d3ba7519..9594cbe2 100644 --- a/api-reference/topics/subscribers-removal.mdx +++ b/api-reference/topics/subscribers-removal.mdx @@ -4,4 +4,4 @@ openapi: post /v1/topics/{topicKey}/subscribers/removal import ApikeyWarning from "/snippets/apikey-warning.mdx"; - \ No newline at end of file + diff --git a/api-reference/topics/topic-creation.mdx b/api-reference/topics/topic-creation.mdx index dc3f11da..160c9f15 100644 --- a/api-reference/topics/topic-creation.mdx +++ b/api-reference/topics/topic-creation.mdx @@ -4,4 +4,4 @@ openapi: post /v1/topics import ApikeyWarning from "/snippets/apikey-warning.mdx"; - \ No newline at end of file + diff --git a/api-reference/workflow-groups/create-workflow-group.mdx b/api-reference/workflow-groups/create-workflow-group.mdx index 999642e9..392ae957 100644 --- a/api-reference/workflow-groups/create-workflow-group.mdx +++ b/api-reference/workflow-groups/create-workflow-group.mdx @@ -16,9 +16,14 @@ curl --request POST \ ``` ```javascript Node.js -import { Novu, TemplateVariableTypeEnum, FilterPartTypeEnum, StepTypeEnum } from '@novu/node'; +import { + Novu, + TemplateVariableTypeEnum, + FilterPartTypeEnum, + StepTypeEnum, +} from "@novu/node"; -const novu = new Novu(''); +const novu = new Novu(""); await novu.notificationGroups.create("Product Updates"); ``` @@ -89,7 +94,7 @@ func main() { body, _ := ioutil.ReadAll(resp.Body) fmt.Println(string(body)) } - + ``` @@ -108,4 +113,4 @@ func main() { } ``` - \ No newline at end of file + diff --git a/api-reference/workflow-groups/delete-workflow-group.mdx b/api-reference/workflow-groups/delete-workflow-group.mdx index c96ae73e..0741804a 100644 --- a/api-reference/workflow-groups/delete-workflow-group.mdx +++ b/api-reference/workflow-groups/delete-workflow-group.mdx @@ -14,9 +14,14 @@ curl --request DELETE \ ``` ```javascript Node.js -import { Novu, TemplateVariableTypeEnum, FilterPartTypeEnum, StepTypeEnum } from '@novu/node'; +import { + Novu, + TemplateVariableTypeEnum, + FilterPartTypeEnum, + StepTypeEnum, +} from "@novu/node"; -const novu = new Novu(''); +const novu = new Novu(""); await novu.notificationGroups.delete("notificationGroupId"); ``` @@ -42,7 +47,7 @@ $result = file_get_contents($url, false, $context); if ($result === FALSE) { /* Handle error */ } echo $result; - + ``` ```ruby Ruby @@ -63,7 +68,7 @@ request.add_field('Authorization', 'ApiKey REPLACE_WITH_API_KEY') response = http.request(request) puts response.body - + ``` ```python Python @@ -72,7 +77,7 @@ import requests response = requests.delete('https://api.novu.co/v1/notification-groups/:id') print(response.json()) - + ``` ```go Go @@ -98,7 +103,7 @@ func main() { body, _ := ioutil.ReadAll(resp.Body) fmt.Println(string(body)) } - + ``` @@ -114,4 +119,4 @@ func main() { } ``` - \ No newline at end of file + diff --git a/api-reference/workflow-groups/get-workflow-group.mdx b/api-reference/workflow-groups/get-workflow-group.mdx index c53604dc..208f0a21 100644 --- a/api-reference/workflow-groups/get-workflow-group.mdx +++ b/api-reference/workflow-groups/get-workflow-group.mdx @@ -14,9 +14,14 @@ curl --request GET \ ``` ```javascript Node.js -import { Novu, TemplateVariableTypeEnum, FilterPartTypeEnum, StepTypeEnum } from '@novu/node'; +import { + Novu, + TemplateVariableTypeEnum, + FilterPartTypeEnum, + StepTypeEnum, +} from "@novu/node"; -const novu = new Novu(''); +const novu = new Novu(""); await novu.notificationGroups.getOne("notificationGroupId"); ``` @@ -42,7 +47,7 @@ $result = file_get_contents($url, false, $context); if ($result === FALSE) { /* Handle error */ } echo $result; - + ``` ```ruby Ruby @@ -63,7 +68,7 @@ request.add_field('Authorization', 'ApiKey REPLACE_WITH_API_KEY') response = http.request(request) puts response.body - + ``` ```python Python @@ -72,7 +77,7 @@ import requests response = requests.get('https://api.novu.co/v1/notification-groups/:id') print(response.json()) - + ``` ```go Go @@ -98,7 +103,7 @@ func main() { body, _ := ioutil.ReadAll(resp.Body) fmt.Println(string(body)) } - + ``` @@ -117,4 +122,4 @@ func main() { } ``` - \ No newline at end of file + diff --git a/api-reference/workflow-groups/get-workflow-groups.mdx b/api-reference/workflow-groups/get-workflow-groups.mdx index 4d16a991..61efb398 100644 --- a/api-reference/workflow-groups/get-workflow-groups.mdx +++ b/api-reference/workflow-groups/get-workflow-groups.mdx @@ -14,9 +14,14 @@ curl --request GET \ ``` ```javascript Node.js -import { Novu, TemplateVariableTypeEnum, FilterPartTypeEnum, StepTypeEnum } from '@novu/node'; +import { + Novu, + TemplateVariableTypeEnum, + FilterPartTypeEnum, + StepTypeEnum, +} from "@novu/node"; -const novu = new Novu(''); +const novu = new Novu(""); await novu.notificationGroups.get(); ``` @@ -82,7 +87,7 @@ func main() { body, _ := ioutil.ReadAll(resp.Body) fmt.Println(string(body)) } - + ``` @@ -103,4 +108,4 @@ func main() { } ``` - \ No newline at end of file + diff --git a/api-reference/workflow-groups/update-workflow-group.mdx b/api-reference/workflow-groups/update-workflow-group.mdx index 2f8b1906..76f6e460 100644 --- a/api-reference/workflow-groups/update-workflow-group.mdx +++ b/api-reference/workflow-groups/update-workflow-group.mdx @@ -16,11 +16,18 @@ curl --request PATCH \ ``` ```javascript Node.js -import { Novu, TemplateVariableTypeEnum, FilterPartTypeEnum, StepTypeEnum } from '@novu/node'; - -const novu = new Novu(''); - -await novu.notificationGroups.update("notificationGroupId", { name: "Changelog Updates"}); +import { + Novu, + TemplateVariableTypeEnum, + FilterPartTypeEnum, + StepTypeEnum, +} from "@novu/node"; + +const novu = new Novu(""); + +await novu.notificationGroups.update("notificationGroupId", { + name: "Changelog Updates", +}); ``` ```php PHP @@ -44,7 +51,7 @@ $result = file_get_contents($url, false, $context); if ($result === FALSE) { /* Handle error */ } echo $result; - + ``` ```ruby Ruby @@ -65,7 +72,7 @@ request.add_field('Authorization', 'ApiKey REPLACE_WITH_API_KEY') response = http.request(request) puts response.body - + ``` ```python Python @@ -79,7 +86,7 @@ response = requests.patch('https://api.novu.co/v1/notification-groups/:id', json }) print(response.json()) - + ``` ```java Java @@ -136,4 +143,4 @@ func main() { } ``` - \ No newline at end of file + diff --git a/api-reference/workflow-overrides/delete-workflow-override.mdx b/api-reference/workflow-overrides/delete-workflow-override.mdx index 3da0deb8..a0d9c37f 100644 --- a/api-reference/workflow-overrides/delete-workflow-override.mdx +++ b/api-reference/workflow-overrides/delete-workflow-override.mdx @@ -2,8 +2,6 @@ openapi: delete /v1/workflow-overrides/{overrideId} --- - import ApikeyWarning from "/snippets/apikey-warning.mdx"; - diff --git a/api-reference/workflow-overrides/get-workflow-override-by-id.mdx b/api-reference/workflow-overrides/get-workflow-override-by-id.mdx index 4c482403..ee223ebe 100644 --- a/api-reference/workflow-overrides/get-workflow-override-by-id.mdx +++ b/api-reference/workflow-overrides/get-workflow-override-by-id.mdx @@ -5,4 +5,3 @@ openapi: get /v1/workflow-overrides/{overrideId} import ApikeyWarning from "/snippets/apikey-warning.mdx"; - diff --git a/api-reference/workflow-overrides/get-workflow-override.mdx b/api-reference/workflow-overrides/get-workflow-override.mdx index f7084444..d4d0c6f0 100644 --- a/api-reference/workflow-overrides/get-workflow-override.mdx +++ b/api-reference/workflow-overrides/get-workflow-override.mdx @@ -5,4 +5,3 @@ openapi: get /v1/workflow-overrides/workflows/{workflowId}/tenants/{tenantId} import ApikeyWarning from "/snippets/apikey-warning.mdx"; - diff --git a/api-reference/workflow-overrides/get-workflow-overrides.mdx b/api-reference/workflow-overrides/get-workflow-overrides.mdx index ee22bcf0..c8813ff3 100644 --- a/api-reference/workflow-overrides/get-workflow-overrides.mdx +++ b/api-reference/workflow-overrides/get-workflow-overrides.mdx @@ -5,4 +5,3 @@ openapi: get /v1/workflow-overrides import ApikeyWarning from "/snippets/apikey-warning.mdx"; - diff --git a/api-reference/workflow-overrides/update-workflow-override-by-id.mdx b/api-reference/workflow-overrides/update-workflow-override-by-id.mdx index 1729f433..15090d6d 100644 --- a/api-reference/workflow-overrides/update-workflow-override-by-id.mdx +++ b/api-reference/workflow-overrides/update-workflow-override-by-id.mdx @@ -5,4 +5,3 @@ openapi: put /v1/workflow-overrides/{overrideId} import ApikeyWarning from "/snippets/apikey-warning.mdx"; - diff --git a/api-reference/workflows/create-workflow.mdx b/api-reference/workflows/create-workflow.mdx index 005bf6b5..a7fa47f4 100644 --- a/api-reference/workflows/create-workflow.mdx +++ b/api-reference/workflows/create-workflow.mdx @@ -21,12 +21,17 @@ import ApikeyWarning from "/snippets/apikey-warning.mdx"; ``` ```javascript Node.js -import { Novu, TemplateVariableTypeEnum, FilterPartTypeEnum, StepTypeEnum } from '@novu/node'; +import { + Novu, + TemplateVariableTypeEnum, + FilterPartTypeEnum, + StepTypeEnum, +} from "@novu/node"; -const novu = new Novu(''); +const novu = new Novu(""); await novu.notificationTemplates.create({ - name: 'Onboarding Workflow', + name: "Onboarding Workflow", // taking first workflow group id notificationGroupId: workflowGroupsData.data[0]._id, steps: [ @@ -35,16 +40,16 @@ await novu.notificationTemplates.create({ active: true, shouldStopOnFail: false, // UUID is optional. - uuid: '78ab8c72-46de-49e4-8464-257085960f9e', - name: 'Chat', + uuid: "78ab8c72-46de-49e4-8464-257085960f9e", + name: "Chat", filters: [ { - value: 'AND', + value: "AND", children: [ { - field: '{{chatContent}}', - value: 'flag', - operator: 'NOT_IN', + field: "{{chatContent}}", + value: "flag", + operator: "NOT_IN", // 'payload' on: FilterPartTypeEnum.PAYLOAD, }, @@ -55,22 +60,22 @@ await novu.notificationTemplates.create({ // 'chat' type: StepTypeEnum.CHAT, active: true, - subject: '', + subject: "", variables: [ { - name: 'chatContent', + name: "chatContent", // 'String' type: TemplateVariableTypeEnum.STRING, required: true, - "defaultValue": "default message", + defaultValue: "default message", }, ], - content: '{{chatContent}}', - contentType: 'editor', + content: "{{chatContent}}", + contentType: "editor", }, }, ], - description: 'Onboarding workflow to trigger after user sign up', + description: "Onboarding workflow to trigger after user sign up", active: true, draft: false, critical: false, @@ -107,7 +112,7 @@ body = { 'description' => 'description', # optional 'steps' => [ # optional # insert all fields here - ], + ], 'active' => true, # optional 'draft' => true, # optional 'critical' => true, # optional @@ -211,9 +216,7 @@ class Main { "push": true }, "critical": true, - "tags": [ - "string" - ], + "tags": ["string"], "steps": [ { "_id": "string", @@ -288,4 +291,5 @@ class Main { } } ``` - \ No newline at end of file + + diff --git a/api-reference/workflows/delete-workflow.mdx b/api-reference/workflows/delete-workflow.mdx index 88ef656a..12b3c756 100644 --- a/api-reference/workflows/delete-workflow.mdx +++ b/api-reference/workflows/delete-workflow.mdx @@ -3,8 +3,8 @@ openapi: delete /v1/workflows/{workflowId} --- --- -openapi: post /v1/workflows ---- + +## openapi: post /v1/workflows import ApikeyWarning from "/snippets/apikey-warning.mdx"; @@ -19,9 +19,14 @@ import ApikeyWarning from "/snippets/apikey-warning.mdx"; ``` ```javascript Node.js -import { Novu, TemplateVariableTypeEnum, FilterPartTypeEnum, StepTypeEnum } from '@novu/node'; +import { + Novu, + TemplateVariableTypeEnum, + FilterPartTypeEnum, + StepTypeEnum, +} from "@novu/node"; -const novu = new Novu(''); +const novu = new Novu(""); await novu.notificationTemplates.delete("workflowId"); ``` @@ -109,8 +114,8 @@ class Main { ```json Response { - "data": true + "data": true } ``` - \ No newline at end of file + diff --git a/api-reference/workflows/get-workflow.mdx b/api-reference/workflows/get-workflow.mdx index d3b87586..4520f06d 100644 --- a/api-reference/workflows/get-workflow.mdx +++ b/api-reference/workflows/get-workflow.mdx @@ -15,9 +15,14 @@ import ApikeyWarning from "/snippets/apikey-warning.mdx"; ``` ```javascript Node.js -import { Novu, TemplateVariableTypeEnum, FilterPartTypeEnum, StepTypeEnum } from '@novu/node'; +import { + Novu, + TemplateVariableTypeEnum, + FilterPartTypeEnum, + StepTypeEnum, +} from "@novu/node"; -const novu = new Novu(''); +const novu = new Novu(""); await novu.notificationTemplates.getOne("workflowIdOrIdentifier"); ``` @@ -104,102 +109,102 @@ class Main { ```json Response { - "data": { - "preferenceSettings": { - "email": true, - "sms": true, - "in_app": true, - "chat": true, - "push": true - }, + "data": { + "preferenceSettings": { + "email": true, + "sms": true, + "in_app": true, + "chat": true, + "push": true + }, + "_id": "7u83d3123t3a9e261ba9934r8", + "name": "chat with Discord", + "active": true, + "draft": false, + "critical": true, + "isBlueprint": false, + "_notificationGroupId": "641eddefda8256b93f750e0b", + "tags": [], + "triggers": [ + { + "type": "event", + "identifier": "chat-with-discord", + "variables": [ + { + "name": "chatMsg", + "type": "String", + "_id": "7u83d3123t3a9e261ba9934r8", + "id": "7u83d3123t3a9e261ba9934r8" + } + ], + "reservedVariables": [], + "subscriberVariables": [], "_id": "7u83d3123t3a9e261ba9934r8", - "name": "chat with Discord", + "id": "7u83d3123t3a9e261ba9934r8" + } + ], + "steps": [ + { + "metadata": { + "timed": { + "weekDays": [], + "monthDays": [] + } + }, "active": true, - "draft": false, - "critical": true, - "isBlueprint": false, - "_notificationGroupId": "641eddefda8256b93f750e0b", - "tags": [], - "triggers": [ - { - "type": "event", - "identifier": "chat-with-discord", - "variables": [ - { - "name": "chatMsg", - "type": "String", - "_id": "7u83d3123t3a9e261ba9934r8", - "id": "7u83d3123t3a9e261ba9934r8" - } - ], - "reservedVariables": [], - "subscriberVariables": [], - "_id": "7u83d3123t3a9e261ba9934r8", - "id": "7u83d3123t3a9e261ba9934r8" - } + "shouldStopOnFail": false, + "uuid": "9799f1c9-0de2-4252-8921-01840a71a741", + "name": "Chat", + "filters": [ + { + "children": [], + "_id": "7u83d3123t3a9e261ba99376e", + "id": "7u83d3123t3a9e261ba993999" + } ], - "steps": [ + "_templateId": "6533d389306c4d8cf7853557", + "_parentId": null, + "_id": "99u3d3123t3a9e261ba993r45", + "id": "90uid3123t3a9e261ba9978yu", + "template": { + "_id": "r567d3123t3a9e261ba9990as", + "type": "chat", + "active": true, + "subject": "", + "variables": [ { - "metadata": { - "timed": { - "weekDays": [], - "monthDays": [] - } - }, - "active": true, - "shouldStopOnFail": false, - "uuid": "9799f1c9-0de2-4252-8921-01840a71a741", - "name": "Chat", - "filters": [ - { - "children": [], - "_id": "7u83d3123t3a9e261ba99376e", - "id": "7u83d3123t3a9e261ba993999" - } - ], - "_templateId": "6533d389306c4d8cf7853557", - "_parentId": null, - "_id": "99u3d3123t3a9e261ba993r45", - "id": "90uid3123t3a9e261ba9978yu", - "template": { - "_id": "r567d3123t3a9e261ba9990as", - "type": "chat", - "active": true, - "subject": "", - "variables": [ - { - "name": "chatMsg", - "type": "String", - "required": false, - "_id": "2383d3123t3a9e261ba997878", - "id": "8711d389306c4d8cf7859090" - } - ], - "content": "{{chatMsg}}", - "contentType": "editor", - "_environmentId": "234edtyuda8256b93f7567f90", - "_organizationId": "333tydefda8256b93f753yh6", - "_creatorId": "63123ee5d9af70da9f6a2e4", - "_feedId": null, - "_layoutId": null, - "deleted": false, - "createdAt": "2023-10-21T13:35:05.825Z", - "updatedAt": "2023-10-21T13:35:18.156Z", - "__v": 0, - "id": "7u83d3123t3a9e261ba9934r8" - } + "name": "chatMsg", + "type": "String", + "required": false, + "_id": "2383d3123t3a9e261ba997878", + "id": "8711d389306c4d8cf7859090" } - ], - "_environmentId": "234edtyuda8256b93f7567f90", - "_organizationId": "333tydefda8256b93f753yh6", - "_creatorId": "63123ee5d9af70da9f6a2e4", - "deleted": false, - "createdAt": "2023-10-21T13:34:48.130Z", - "updatedAt": "2023-10-21T13:35:18.994Z", - "__v": 0, - "id": "7u83d3123t3a9e261ba9934r8" - } + ], + "content": "{{chatMsg}}", + "contentType": "editor", + "_environmentId": "234edtyuda8256b93f7567f90", + "_organizationId": "333tydefda8256b93f753yh6", + "_creatorId": "63123ee5d9af70da9f6a2e4", + "_feedId": null, + "_layoutId": null, + "deleted": false, + "createdAt": "2023-10-21T13:35:05.825Z", + "updatedAt": "2023-10-21T13:35:18.156Z", + "__v": 0, + "id": "7u83d3123t3a9e261ba9934r8" + } + } + ], + "_environmentId": "234edtyuda8256b93f7567f90", + "_organizationId": "333tydefda8256b93f753yh6", + "_creatorId": "63123ee5d9af70da9f6a2e4", + "deleted": false, + "createdAt": "2023-10-21T13:34:48.130Z", + "updatedAt": "2023-10-21T13:35:18.994Z", + "__v": 0, + "id": "7u83d3123t3a9e261ba9934r8" + } } ``` - \ No newline at end of file + diff --git a/api-reference/workflows/get-workflows.mdx b/api-reference/workflows/get-workflows.mdx index 04a95cc4..8f874a23 100644 --- a/api-reference/workflows/get-workflows.mdx +++ b/api-reference/workflows/get-workflows.mdx @@ -15,13 +15,18 @@ import ApikeyWarning from "/snippets/apikey-warning.mdx"; ``` ```javascript Node.js -import { Novu, TemplateVariableTypeEnum, FilterPartTypeEnum, StepTypeEnum } from '@novu/node'; +import { + Novu, + TemplateVariableTypeEnum, + FilterPartTypeEnum, + StepTypeEnum, +} from "@novu/node"; -const novu = new Novu(''); +const novu = new Novu(""); await novu.notificationTemplates.getAll({ page: 0, // optional - limit: 20 // optional + limit: 20, // optional }); ``` @@ -88,7 +93,7 @@ func main() { body, _ := ioutil.ReadAll(resp.Body) fmt.Println(string(body)) } - + ``` ```kotlin Kotlin @@ -111,1030 +116,1030 @@ class Main { ```json Response { - "page": 0, - "data": [ + "page": 0, + "data": [ + { + "_id": "64f07ea63f28d769494bd74a", + "name": "Chat with Novu", + "active": true, + "draft": false, + "critical": true, + "isBlueprint": false, + "_notificationGroupId": "641eddefda8256b93f750e0b", + "tags": [], + "triggers": [ { - "_id": "64f07ea63f28d769494bd74a", - "name": "Chat with Novu", - "active": true, - "draft": false, - "critical": true, - "isBlueprint": false, - "_notificationGroupId": "641eddefda8256b93f750e0b", - "tags": [], - "triggers": [ - { - "type": "event", - "identifier": "sms-docs", - "variables": [ - { - "name": "payloadVar", - "type": "String", - "_id": "64f1df7a97770dc90cf8c951" - } - ], - "subscriberVariables": [], - "_id": "64f07ea63f28d769494bd74b", - "reservedVariables": [] - } - ], - "steps": [ - { - "active": true, - "shouldStopOnFail": false, - "uuid": "0c22b8e5-762f-49b3-b686-09edb2275e8d", - "name": "Chat", - "filters": [ - { - "children": [], - "_id": "64f1ddba1dde0257bca4352d" - } - ], - "_templateId": "64f1ddba1dde0257bca4351d", - "_parentId": null, - "metadata": { - "timed": { - "weekDays": [], - "monthDays": [] - } - }, - "_id": "64f1ddba1dde0257bca4351d", - "template": { - "_id": "64f1ddba1dde0257bca4351d", - "type": "chat" - } - } - ], - "preferenceSettings": { - "email": true, - "sms": true, - "in_app": true, - "chat": true, - "push": true - }, - "_environmentId": "641eddefda8256b93f750e07", - "_organizationId": "641eddefda8256b93f750e01", - "_creatorId": "641eddee5d9af70da9f6a2e4", - "deleted": false, - "createdAt": "2023-08-31T11:51:02.984Z", - "updatedAt": "2023-09-01T12:56:26.881Z", - "__v": 0, - "notificationGroup": { - "_id": "641eddefda8256b93f750e0b", - "name": "General", - "_organizationId": "641eddefda8256b93f750e01", - "_environmentId": "641eddefda8256b93f750e07", - "createdAt": "2023-03-25T11:41:35.279Z", - "updatedAt": "2023-03-25T11:41:35.279Z", - "__v": 0 - }, - "workflowIntegrationStatus": { - "hasActiveIntegrations": true, - "channels": { - "in_app": { - "hasActiveIntegrations": true - }, - "email": { - "hasActiveIntegrations": true, - "hasPrimaryIntegrations": true - }, - "sms": { - "hasActiveIntegrations": true, - "hasPrimaryIntegrations": true - }, - "chat": { - "hasActiveIntegrations": true - }, - "push": { - "hasActiveIntegrations": false - } - } + "type": "event", + "identifier": "sms-docs", + "variables": [ + { + "name": "payloadVar", + "type": "String", + "_id": "64f1df7a97770dc90cf8c951" } - }, + ], + "subscriberVariables": [], + "_id": "64f07ea63f28d769494bd74b", + "reservedVariables": [] + } + ], + "steps": [ { - "_id": "64f024843f28d7694946d097", - "name": "in-app workflow", - "active": true, - "draft": false, - "critical": true, - "isBlueprint": false, - "_notificationGroupId": "641eddefda8256b93f750e0b", - "tags": [], - "triggers": [ - { - "type": "event", - "identifier": "in-app-workflow", - "variables": [ - { - "name": "messageIdentifier", - "type": "String", - "_id": "64f02b28dd99c21f1e2d4ac7" - } - ], - "subscriberVariables": [], - "_id": "64f024843f28d7694946d098" - } - ], - "steps": [ - { - "active": true, - "shouldStopOnFail": false, - "uuid": "ba437658-522d-439f-94da-02426b42d616", - "name": "In-App", - "filters": [ - { - "children": [], - "_id": "64f02b1914b8a3ace6186985" - } - ], - "_templateId": "64f02b1914b8a3ace618697a", - "_parentId": null, - "metadata": { - "timed": { - "weekDays": [], - "monthDays": [] - } - }, - "_id": "64f02b1914b8a3ace618697a", - "template": { - "_id": "64f02b1914b8a3ace618697a", - "type": "in_app" - } - } - ], - "preferenceSettings": { - "email": true, - "sms": true, - "in_app": true, - "chat": true, - "push": true - }, - "_environmentId": "641eddefda8256b93f750e07", - "_organizationId": "641eddefda8256b93f750e01", - "_creatorId": "641eddee5d9af70da9f6a2e4", - "deleted": false, - "createdAt": "2023-08-31T05:26:28.637Z", - "updatedAt": "2023-08-31T05:54:48.445Z", - "__v": 0, - "notificationGroup": { - "_id": "641eddefda8256b93f750e0b", - "name": "General", - "_organizationId": "641eddefda8256b93f750e01", - "_environmentId": "641eddefda8256b93f750e07", - "createdAt": "2023-03-25T11:41:35.279Z", - "updatedAt": "2023-03-25T11:41:35.279Z", - "__v": 0 - }, - "workflowIntegrationStatus": { - "hasActiveIntegrations": true, - "channels": { - "in_app": { - "hasActiveIntegrations": true - }, - "email": { - "hasActiveIntegrations": true, - "hasPrimaryIntegrations": true - }, - "sms": { - "hasActiveIntegrations": true, - "hasPrimaryIntegrations": true - }, - "chat": { - "hasActiveIntegrations": true - }, - "push": { - "hasActiveIntegrations": false - } - } + "active": true, + "shouldStopOnFail": false, + "uuid": "0c22b8e5-762f-49b3-b686-09edb2275e8d", + "name": "Chat", + "filters": [ + { + "children": [], + "_id": "64f1ddba1dde0257bca4352d" } - }, + ], + "_templateId": "64f1ddba1dde0257bca4351d", + "_parentId": null, + "metadata": { + "timed": { + "weekDays": [], + "monthDays": [] + } + }, + "_id": "64f1ddba1dde0257bca4351d", + "template": { + "_id": "64f1ddba1dde0257bca4351d", + "type": "chat" + } + } + ], + "preferenceSettings": { + "email": true, + "sms": true, + "in_app": true, + "chat": true, + "push": true + }, + "_environmentId": "641eddefda8256b93f750e07", + "_organizationId": "641eddefda8256b93f750e01", + "_creatorId": "641eddee5d9af70da9f6a2e4", + "deleted": false, + "createdAt": "2023-08-31T11:51:02.984Z", + "updatedAt": "2023-09-01T12:56:26.881Z", + "__v": 0, + "notificationGroup": { + "_id": "641eddefda8256b93f750e0b", + "name": "General", + "_organizationId": "641eddefda8256b93f750e01", + "_environmentId": "641eddefda8256b93f750e07", + "createdAt": "2023-03-25T11:41:35.279Z", + "updatedAt": "2023-03-25T11:41:35.279Z", + "__v": 0 + }, + "workflowIntegrationStatus": { + "hasActiveIntegrations": true, + "channels": { + "in_app": { + "hasActiveIntegrations": true + }, + "email": { + "hasActiveIntegrations": true, + "hasPrimaryIntegrations": true + }, + "sms": { + "hasActiveIntegrations": true, + "hasPrimaryIntegrations": true + }, + "chat": { + "hasActiveIntegrations": true + }, + "push": { + "hasActiveIntegrations": false + } + } + } + }, + { + "_id": "64f024843f28d7694946d097", + "name": "in-app workflow", + "active": true, + "draft": false, + "critical": true, + "isBlueprint": false, + "_notificationGroupId": "641eddefda8256b93f750e0b", + "tags": [], + "triggers": [ { - "_id": "64eec9583cd25fad61223897", - "name": "Discord chat demo", - "active": true, - "draft": false, - "critical": true, - "isBlueprint": false, - "_notificationGroupId": "641eddefda8256b93f750e0b", - "tags": [], - "triggers": [ - { - "type": "event", - "identifier": "discord-chat-demo", - "variables": [ - { - "name": "chatMsg", - "type": "String", - "_id": "64eed56c735c8d00d98d2a71" - } - ], - "subscriberVariables": [], - "_id": "64eec9583cd25fad61223898" - } - ], - "steps": [ - { - "active": true, - "shouldStopOnFail": false, - "uuid": "03bb2304-d18c-4dc1-ae5a-62fe2634e787", - "name": "Chat", - "filters": [ - { - "children": [], - "_id": "64eeca4faed841efdcaf95eb" - } - ], - "_templateId": "64eeca4faed841efdcaf95e2", - "_parentId": null, - "metadata": { - "timed": { - "weekDays": [], - "monthDays": [] - } - }, - "_id": "64eeca4faed841efdcaf95e2", - "template": { - "_id": "64eeca4faed841efdcaf95e2", - "type": "chat" - } - } - ], - "preferenceSettings": { - "email": true, - "sms": true, - "in_app": true, - "chat": true, - "push": true - }, - "_environmentId": "641eddefda8256b93f750e07", - "_organizationId": "641eddefda8256b93f750e01", - "_creatorId": "641eddee5d9af70da9f6a2e4", - "deleted": false, - "createdAt": "2023-08-30T04:45:12.419Z", - "updatedAt": "2023-08-30T05:36:44.465Z", - "__v": 0, - "notificationGroup": { - "_id": "641eddefda8256b93f750e0b", - "name": "General", - "_organizationId": "641eddefda8256b93f750e01", - "_environmentId": "641eddefda8256b93f750e07", - "createdAt": "2023-03-25T11:41:35.279Z", - "updatedAt": "2023-03-25T11:41:35.279Z", - "__v": 0 - }, - "workflowIntegrationStatus": { - "hasActiveIntegrations": true, - "channels": { - "in_app": { - "hasActiveIntegrations": true - }, - "email": { - "hasActiveIntegrations": true, - "hasPrimaryIntegrations": true - }, - "sms": { - "hasActiveIntegrations": true, - "hasPrimaryIntegrations": true - }, - "chat": { - "hasActiveIntegrations": true - }, - "push": { - "hasActiveIntegrations": false - } - } + "type": "event", + "identifier": "in-app-workflow", + "variables": [ + { + "name": "messageIdentifier", + "type": "String", + "_id": "64f02b28dd99c21f1e2d4ac7" } - }, + ], + "subscriberVariables": [], + "_id": "64f024843f28d7694946d098" + } + ], + "steps": [ { - "_id": "64db9511c51d09cdd01ff086", - "name": "testing from browser", - "active": true, - "draft": false, - "critical": true, - "isBlueprint": false, - "_notificationGroupId": "641eddefda8256b93f750e0b", - "tags": [], - "triggers": [ - { - "type": "event", - "identifier": "testing-from-browser", - "variables": [], - "subscriberVariables": [], - "_id": "64db9511c51d09cdd01ff087" - } - ], - "steps": [ - { - "active": true, - "shouldStopOnFail": false, - "uuid": "29a67140-4694-443b-b4e1-8111b399198a", - "name": "testting browser", - "filters": [ - { - "children": [], - "_id": "64db954a98ca413e3d6838a7" - } - ], - "_templateId": "64db954a98ca413e3d68389d", - "_parentId": null, - "metadata": { - "timed": { - "weekDays": [], - "monthDays": [] - } - }, - "_id": "64db954a98ca413e3d68389d", - "template": { - "_id": "64db954a98ca413e3d68389d", - "type": "in_app" - } - } - ], - "preferenceSettings": { - "email": true, - "sms": true, - "in_app": true, - "chat": true, - "push": true - }, - "_environmentId": "641eddefda8256b93f750e07", - "_organizationId": "641eddefda8256b93f750e01", - "_creatorId": "641eddee5d9af70da9f6a2e4", - "deleted": false, - "createdAt": "2023-08-15T15:09:05.828Z", - "updatedAt": "2023-08-25T11:55:48.583Z", - "__v": 0, - "notificationGroup": { - "_id": "641eddefda8256b93f750e0b", - "name": "General", - "_organizationId": "641eddefda8256b93f750e01", - "_environmentId": "641eddefda8256b93f750e07", - "createdAt": "2023-03-25T11:41:35.279Z", - "updatedAt": "2023-03-25T11:41:35.279Z", - "__v": 0 - }, - "workflowIntegrationStatus": { - "hasActiveIntegrations": true, - "channels": { - "in_app": { - "hasActiveIntegrations": true - }, - "email": { - "hasActiveIntegrations": true, - "hasPrimaryIntegrations": true - }, - "sms": { - "hasActiveIntegrations": true, - "hasPrimaryIntegrations": true - }, - "chat": { - "hasActiveIntegrations": true - }, - "push": { - "hasActiveIntegrations": false - } - } + "active": true, + "shouldStopOnFail": false, + "uuid": "ba437658-522d-439f-94da-02426b42d616", + "name": "In-App", + "filters": [ + { + "children": [], + "_id": "64f02b1914b8a3ace6186985" } - }, + ], + "_templateId": "64f02b1914b8a3ace618697a", + "_parentId": null, + "metadata": { + "timed": { + "weekDays": [], + "monthDays": [] + } + }, + "_id": "64f02b1914b8a3ace618697a", + "template": { + "_id": "64f02b1914b8a3ace618697a", + "type": "in_app" + } + } + ], + "preferenceSettings": { + "email": true, + "sms": true, + "in_app": true, + "chat": true, + "push": true + }, + "_environmentId": "641eddefda8256b93f750e07", + "_organizationId": "641eddefda8256b93f750e01", + "_creatorId": "641eddee5d9af70da9f6a2e4", + "deleted": false, + "createdAt": "2023-08-31T05:26:28.637Z", + "updatedAt": "2023-08-31T05:54:48.445Z", + "__v": 0, + "notificationGroup": { + "_id": "641eddefda8256b93f750e0b", + "name": "General", + "_organizationId": "641eddefda8256b93f750e01", + "_environmentId": "641eddefda8256b93f750e07", + "createdAt": "2023-03-25T11:41:35.279Z", + "updatedAt": "2023-03-25T11:41:35.279Z", + "__v": 0 + }, + "workflowIntegrationStatus": { + "hasActiveIntegrations": true, + "channels": { + "in_app": { + "hasActiveIntegrations": true + }, + "email": { + "hasActiveIntegrations": true, + "hasPrimaryIntegrations": true + }, + "sms": { + "hasActiveIntegrations": true, + "hasPrimaryIntegrations": true + }, + "chat": { + "hasActiveIntegrations": true + }, + "push": { + "hasActiveIntegrations": false + } + } + } + }, + { + "_id": "64eec9583cd25fad61223897", + "name": "Discord chat demo", + "active": true, + "draft": false, + "critical": true, + "isBlueprint": false, + "_notificationGroupId": "641eddefda8256b93f750e0b", + "tags": [], + "triggers": [ { - "_id": "64c0e3b9ec5b16b5d790d55a", - "name": "headless-demo", - "active": true, - "draft": false, - "critical": true, - "isBlueprint": false, - "_notificationGroupId": "641eddefda8256b93f750e0b", - "tags": [], - "triggers": [ - { - "type": "event", - "identifier": "headless-demo", - "variables": [ - { - "name": "description", - "type": "String", - "_id": "64c0e3d2ec5b16b5d790d5f6" - } - ], - "subscriberVariables": [], - "_id": "64c0e3b9ec5b16b5d790d55b" - } - ], - "steps": [ - { - "active": true, - "shouldStopOnFail": false, - "uuid": "e0ba19d2-7833-4525-971c-4f40dd47e2ff", - "name": "In-App", - "filters": [ - { - "children": [], - "_id": "64c0e3d2ec5b16b5d790d5f5" - } - ], - "_templateId": "64c0e3d2ec5b16b5d790d5ea", - "_parentId": null, - "metadata": { - "timed": { - "weekDays": [], - "monthDays": [] - } - }, - "_id": "64c0e3d2ec5b16b5d790d5ea", - "template": { - "_id": "64c0e3d2ec5b16b5d790d5ea", - "type": "in_app" - } - } - ], - "preferenceSettings": { - "email": true, - "sms": true, - "in_app": true, - "chat": true, - "push": true - }, - "_environmentId": "641eddefda8256b93f750e07", - "_organizationId": "641eddefda8256b93f750e01", - "_creatorId": "641eddee5d9af70da9f6a2e4", - "deleted": false, - "createdAt": "2023-07-26T09:13:29.090Z", - "updatedAt": "2023-07-26T09:13:54.856Z", - "__v": 0, - "notificationGroup": { - "_id": "641eddefda8256b93f750e0b", - "name": "General", - "_organizationId": "641eddefda8256b93f750e01", - "_environmentId": "641eddefda8256b93f750e07", - "createdAt": "2023-03-25T11:41:35.279Z", - "updatedAt": "2023-03-25T11:41:35.279Z", - "__v": 0 - }, - "workflowIntegrationStatus": { - "hasActiveIntegrations": true, - "channels": { - "in_app": { - "hasActiveIntegrations": true - }, - "email": { - "hasActiveIntegrations": true, - "hasPrimaryIntegrations": true - }, - "sms": { - "hasActiveIntegrations": true, - "hasPrimaryIntegrations": true - }, - "chat": { - "hasActiveIntegrations": true - }, - "push": { - "hasActiveIntegrations": false - } - } + "type": "event", + "identifier": "discord-chat-demo", + "variables": [ + { + "name": "chatMsg", + "type": "String", + "_id": "64eed56c735c8d00d98d2a71" } - }, + ], + "subscriberVariables": [], + "_id": "64eec9583cd25fad61223898" + } + ], + "steps": [ { - "_id": "64a41ffee2134f0077e0d222", - "name": "emailDigestWorkflow", - "active": true, - "draft": false, - "critical": false, - "isBlueprint": false, - "_notificationGroupId": "641eddefda8256b93f750e0b", - "tags": [], - "triggers": [ - { - "type": "event", - "identifier": "emaildigestworkflow", - "variables": [], - "subscriberVariables": [ - { - "name": "email", - "_id": "64b8f8c024a8ba51900fece6" - } - ], - "_id": "64a41ffee2134f0077e0d223" - } - ], - "steps": [ - { - "active": true, - "shouldStopOnFail": false, - "uuid": "13b27589-a7ad-47e3-b10d-a1085dd3687d", - "name": "Digest", - "filters": [], - "_templateId": "64a421ae5f8a9af84368c9fe", - "_parentId": null, - "metadata": { - "amount": 20, - "unit": "seconds", - "type": "regular", - "backoffAmount": null, - "backoff": false, - "timed": { - "weekDays": [], - "monthDays": [] - } - }, - "_id": "64a421ae5f8a9af84368c9fe", - "template": { - "_id": "64a421ae5f8a9af84368c9fe", - "type": "digest" - } - }, - { - "active": true, - "replyCallback": { - "active": false - }, - "shouldStopOnFail": false, - "uuid": "4e1d8ebb-355c-4224-8a79-fcbc3c0233a1", - "name": "Email", - "filters": [ - { - "children": [], - "_id": "64a420215f8a9af84368b6b8" - } - ], - "_templateId": "64a420215f8a9af84368b6a6", - "_parentId": "64a421ae5f8a9af84368c9fe", - "metadata": { - "timed": { - "weekDays": [], - "monthDays": [] - } - }, - "_id": "64a420215f8a9af84368b6a6", - "template": { - "_id": "64a420215f8a9af84368b6a6", - "type": "email" - } - } - ], - "preferenceSettings": { - "email": true, - "sms": true, - "in_app": true, - "chat": true, - "push": true - }, - "_environmentId": "641eddefda8256b93f750e07", - "_organizationId": "641eddefda8256b93f750e01", - "_creatorId": "641eddee5d9af70da9f6a2e4", - "deleted": false, - "createdAt": "2023-07-04T13:34:54.781Z", - "updatedAt": "2023-07-20T09:05:04.701Z", - "__v": 0, - "notificationGroup": { - "_id": "641eddefda8256b93f750e0b", - "name": "General", - "_organizationId": "641eddefda8256b93f750e01", - "_environmentId": "641eddefda8256b93f750e07", - "createdAt": "2023-03-25T11:41:35.279Z", - "updatedAt": "2023-03-25T11:41:35.279Z", - "__v": 0 - }, - "workflowIntegrationStatus": { - "hasActiveIntegrations": true, - "channels": { - "in_app": { - "hasActiveIntegrations": true - }, - "email": { - "hasActiveIntegrations": true, - "hasPrimaryIntegrations": true - }, - "sms": { - "hasActiveIntegrations": true, - "hasPrimaryIntegrations": true - }, - "chat": { - "hasActiveIntegrations": true - }, - "push": { - "hasActiveIntegrations": false - } - } + "active": true, + "shouldStopOnFail": false, + "uuid": "03bb2304-d18c-4dc1-ae5a-62fe2634e787", + "name": "Chat", + "filters": [ + { + "children": [], + "_id": "64eeca4faed841efdcaf95eb" } - }, + ], + "_templateId": "64eeca4faed841efdcaf95e2", + "_parentId": null, + "metadata": { + "timed": { + "weekDays": [], + "monthDays": [] + } + }, + "_id": "64eeca4faed841efdcaf95e2", + "template": { + "_id": "64eeca4faed841efdcaf95e2", + "type": "chat" + } + } + ], + "preferenceSettings": { + "email": true, + "sms": true, + "in_app": true, + "chat": true, + "push": true + }, + "_environmentId": "641eddefda8256b93f750e07", + "_organizationId": "641eddefda8256b93f750e01", + "_creatorId": "641eddee5d9af70da9f6a2e4", + "deleted": false, + "createdAt": "2023-08-30T04:45:12.419Z", + "updatedAt": "2023-08-30T05:36:44.465Z", + "__v": 0, + "notificationGroup": { + "_id": "641eddefda8256b93f750e0b", + "name": "General", + "_organizationId": "641eddefda8256b93f750e01", + "_environmentId": "641eddefda8256b93f750e07", + "createdAt": "2023-03-25T11:41:35.279Z", + "updatedAt": "2023-03-25T11:41:35.279Z", + "__v": 0 + }, + "workflowIntegrationStatus": { + "hasActiveIntegrations": true, + "channels": { + "in_app": { + "hasActiveIntegrations": true + }, + "email": { + "hasActiveIntegrations": true, + "hasPrimaryIntegrations": true + }, + "sms": { + "hasActiveIntegrations": true, + "hasPrimaryIntegrations": true + }, + "chat": { + "hasActiveIntegrations": true + }, + "push": { + "hasActiveIntegrations": false + } + } + } + }, + { + "_id": "64db9511c51d09cdd01ff086", + "name": "testing from browser", + "active": true, + "draft": false, + "critical": true, + "isBlueprint": false, + "_notificationGroupId": "641eddefda8256b93f750e0b", + "tags": [], + "triggers": [ { - "_id": "6492d92f616db8f05a982022", - "name": "digest showcase", - "active": true, - "draft": false, - "critical": true, - "isBlueprint": false, - "_notificationGroupId": "641eddefda8256b93f750e0b", - "tags": [], - "triggers": [ - { - "type": "event", - "identifier": "digest-showcase", - "variables": [], - "subscriberVariables": [], - "_id": "6492d92f616db8f05a982023" - } - ], - "steps": [ - { - "active": true, - "shouldStopOnFail": false, - "uuid": "556f0514-01b6-46f9-9e3d-72a4c060af54", - "name": "Digest", - "filters": [], - "_templateId": "6492d9735da9bfaf640771a2", - "_parentId": null, - "metadata": { - "amount": 10, - "unit": "seconds", - "type": "regular", - "backoffAmount": null, - "backoff": false, - "timed": { - "weekDays": [], - "monthDays": [] - } - }, - "_id": "6492d9735da9bfaf640771a2", - "template": { - "_id": "6492d9735da9bfaf640771a2", - "type": "digest" - } - }, - { - "active": true, - "shouldStopOnFail": false, - "uuid": "024a27f4-82da-485c-b4a7-bea05c2631e3", - "name": "In-App", - "filters": [ - { - "children": [], - "_id": "6492d9735da9bfaf640771b6" - } - ], - "_templateId": "6492d9735da9bfaf640771aa", - "_parentId": "6492d9735da9bfaf640771a2", - "metadata": { - "timed": { - "weekDays": [], - "monthDays": [] - } - }, - "_id": "6492d9735da9bfaf640771aa", - "template": { - "_id": "6492d9735da9bfaf640771aa", - "type": "in_app" - } - } - ], - "preferenceSettings": { - "email": true, - "sms": true, - "in_app": true, - "chat": true, - "push": true - }, - "_environmentId": "641eddefda8256b93f750e07", - "_organizationId": "641eddefda8256b93f750e01", - "_creatorId": "641eddee5d9af70da9f6a2e4", - "deleted": false, - "createdAt": "2023-06-21T11:04:15.546Z", - "updatedAt": "2023-07-19T13:32:18.474Z", - "__v": 0, - "notificationGroup": { - "_id": "641eddefda8256b93f750e0b", - "name": "General", - "_organizationId": "641eddefda8256b93f750e01", - "_environmentId": "641eddefda8256b93f750e07", - "createdAt": "2023-03-25T11:41:35.279Z", - "updatedAt": "2023-03-25T11:41:35.279Z", - "__v": 0 - }, - "workflowIntegrationStatus": { - "hasActiveIntegrations": true, - "channels": { - "in_app": { - "hasActiveIntegrations": true - }, - "email": { - "hasActiveIntegrations": true, - "hasPrimaryIntegrations": true - }, - "sms": { - "hasActiveIntegrations": true, - "hasPrimaryIntegrations": true - }, - "chat": { - "hasActiveIntegrations": true - }, - "push": { - "hasActiveIntegrations": false - } - } + "type": "event", + "identifier": "testing-from-browser", + "variables": [], + "subscriberVariables": [], + "_id": "64db9511c51d09cdd01ff087" + } + ], + "steps": [ + { + "active": true, + "shouldStopOnFail": false, + "uuid": "29a67140-4694-443b-b4e1-8111b399198a", + "name": "testting browser", + "filters": [ + { + "children": [], + "_id": "64db954a98ca413e3d6838a7" } - }, + ], + "_templateId": "64db954a98ca413e3d68389d", + "_parentId": null, + "metadata": { + "timed": { + "weekDays": [], + "monthDays": [] + } + }, + "_id": "64db954a98ca413e3d68389d", + "template": { + "_id": "64db954a98ca413e3d68389d", + "type": "in_app" + } + } + ], + "preferenceSettings": { + "email": true, + "sms": true, + "in_app": true, + "chat": true, + "push": true + }, + "_environmentId": "641eddefda8256b93f750e07", + "_organizationId": "641eddefda8256b93f750e01", + "_creatorId": "641eddee5d9af70da9f6a2e4", + "deleted": false, + "createdAt": "2023-08-15T15:09:05.828Z", + "updatedAt": "2023-08-25T11:55:48.583Z", + "__v": 0, + "notificationGroup": { + "_id": "641eddefda8256b93f750e0b", + "name": "General", + "_organizationId": "641eddefda8256b93f750e01", + "_environmentId": "641eddefda8256b93f750e07", + "createdAt": "2023-03-25T11:41:35.279Z", + "updatedAt": "2023-03-25T11:41:35.279Z", + "__v": 0 + }, + "workflowIntegrationStatus": { + "hasActiveIntegrations": true, + "channels": { + "in_app": { + "hasActiveIntegrations": true + }, + "email": { + "hasActiveIntegrations": true, + "hasPrimaryIntegrations": true + }, + "sms": { + "hasActiveIntegrations": true, + "hasPrimaryIntegrations": true + }, + "chat": { + "hasActiveIntegrations": true + }, + "push": { + "hasActiveIntegrations": false + } + } + } + }, + { + "_id": "64c0e3b9ec5b16b5d790d55a", + "name": "headless-demo", + "active": true, + "draft": false, + "critical": true, + "isBlueprint": false, + "_notificationGroupId": "641eddefda8256b93f750e0b", + "tags": [], + "triggers": [ { - "_id": "6471aa6f3a4ec8a599669991", - "name": "quickstart", - "active": true, - "draft": false, - "critical": false, - "isBlueprint": false, - "_notificationGroupId": "641eddefda8256b93f750e0b", - "tags": [], - "triggers": [ - { - "type": "event", - "identifier": "quickstart", - "variables": [ - { - "name": "description", - "type": "String", - "_id": "6471aaa08b0b51ed173ba8fd" - } - ], - "subscriberVariables": [], - "_id": "6471aa6f3a4ec8a599669992" - } - ], - "steps": [ - { - "active": true, - "shouldStopOnFail": false, - "uuid": "982bf6a1-6f0c-46c6-a8ad-6daa67f327e2", - "name": "In-App", - "filters": [ - { - "children": [], - "_id": "6471aa85720b54f89ed97de3" - } - ], - "_templateId": "6471aa85720b54f89ed97dd8", - "_parentId": null, - "metadata": { - "timed": { - "weekDays": [], - "monthDays": [] - } - }, - "_id": "6471aa85720b54f89ed97dd8", - "template": { - "_id": "6471aa85720b54f89ed97dd8", - "type": "in_app" - } - } - ], - "preferenceSettings": { - "email": true, - "sms": true, - "in_app": true, - "chat": true, - "push": true - }, - "_environmentId": "641eddefda8256b93f750e07", - "_organizationId": "641eddefda8256b93f750e01", - "_creatorId": "641eddee5d9af70da9f6a2e4", - "deleted": false, - "createdAt": "2023-05-27T06:59:59.254Z", - "updatedAt": "2023-05-27T07:00:48.618Z", - "__v": 0, - "notificationGroup": { - "_id": "641eddefda8256b93f750e0b", - "name": "General", - "_organizationId": "641eddefda8256b93f750e01", - "_environmentId": "641eddefda8256b93f750e07", - "createdAt": "2023-03-25T11:41:35.279Z", - "updatedAt": "2023-03-25T11:41:35.279Z", - "__v": 0 - }, - "workflowIntegrationStatus": { - "hasActiveIntegrations": true, - "channels": { - "in_app": { - "hasActiveIntegrations": true - }, - "email": { - "hasActiveIntegrations": true, - "hasPrimaryIntegrations": true - }, - "sms": { - "hasActiveIntegrations": true, - "hasPrimaryIntegrations": true - }, - "chat": { - "hasActiveIntegrations": true - }, - "push": { - "hasActiveIntegrations": false - } - } + "type": "event", + "identifier": "headless-demo", + "variables": [ + { + "name": "description", + "type": "String", + "_id": "64c0e3d2ec5b16b5d790d5f6" + } + ], + "subscriberVariables": [], + "_id": "64c0e3b9ec5b16b5d790d55b" + } + ], + "steps": [ + { + "active": true, + "shouldStopOnFail": false, + "uuid": "e0ba19d2-7833-4525-971c-4f40dd47e2ff", + "name": "In-App", + "filters": [ + { + "children": [], + "_id": "64c0e3d2ec5b16b5d790d5f5" + } + ], + "_templateId": "64c0e3d2ec5b16b5d790d5ea", + "_parentId": null, + "metadata": { + "timed": { + "weekDays": [], + "monthDays": [] } + }, + "_id": "64c0e3d2ec5b16b5d790d5ea", + "template": { + "_id": "64c0e3d2ec5b16b5d790d5ea", + "type": "in_app" + } + } + ], + "preferenceSettings": { + "email": true, + "sms": true, + "in_app": true, + "chat": true, + "push": true + }, + "_environmentId": "641eddefda8256b93f750e07", + "_organizationId": "641eddefda8256b93f750e01", + "_creatorId": "641eddee5d9af70da9f6a2e4", + "deleted": false, + "createdAt": "2023-07-26T09:13:29.090Z", + "updatedAt": "2023-07-26T09:13:54.856Z", + "__v": 0, + "notificationGroup": { + "_id": "641eddefda8256b93f750e0b", + "name": "General", + "_organizationId": "641eddefda8256b93f750e01", + "_environmentId": "641eddefda8256b93f750e07", + "createdAt": "2023-03-25T11:41:35.279Z", + "updatedAt": "2023-03-25T11:41:35.279Z", + "__v": 0 + }, + "workflowIntegrationStatus": { + "hasActiveIntegrations": true, + "channels": { + "in_app": { + "hasActiveIntegrations": true + }, + "email": { + "hasActiveIntegrations": true, + "hasPrimaryIntegrations": true + }, + "sms": { + "hasActiveIntegrations": true, + "hasPrimaryIntegrations": true + }, + "chat": { + "hasActiveIntegrations": true + }, + "push": { + "hasActiveIntegrations": false + } + } + } + }, + { + "_id": "64a41ffee2134f0077e0d222", + "name": "emailDigestWorkflow", + "active": true, + "draft": false, + "critical": false, + "isBlueprint": false, + "_notificationGroupId": "641eddefda8256b93f750e0b", + "tags": [], + "triggers": [ + { + "type": "event", + "identifier": "emaildigestworkflow", + "variables": [], + "subscriberVariables": [ + { + "name": "email", + "_id": "64b8f8c024a8ba51900fece6" + } + ], + "_id": "64a41ffee2134f0077e0d223" + } + ], + "steps": [ + { + "active": true, + "shouldStopOnFail": false, + "uuid": "13b27589-a7ad-47e3-b10d-a1085dd3687d", + "name": "Digest", + "filters": [], + "_templateId": "64a421ae5f8a9af84368c9fe", + "_parentId": null, + "metadata": { + "amount": 20, + "unit": "seconds", + "type": "regular", + "backoffAmount": null, + "backoff": false, + "timed": { + "weekDays": [], + "monthDays": [] + } + }, + "_id": "64a421ae5f8a9af84368c9fe", + "template": { + "_id": "64a421ae5f8a9af84368c9fe", + "type": "digest" + } }, { - "_id": "6469fc87301d88d6d2b74741", - "name": "Email quickstart", - "active": true, - "draft": false, - "critical": false, - "isBlueprint": false, - "_notificationGroupId": "641eddefda8256b93f750e0b", - "tags": [], - "triggers": [ - { - "type": "event", - "identifier": "email-quickstart", - "variables": [ - { - "name": "email", - "type": "String", - "_id": "646a031c25b1fb30f274ac35" - }, - { - "name": "description", - "type": "String", - "_id": "646a031c25b1fb30f274ac36" - } - ], - "subscriberVariables": [ - { - "name": "email", - "_id": "646a031c25b1fb30f274ac34" - } - ], - "_id": "6469fc87301d88d6d2b74742" - } - ], - "steps": [ - { - "active": true, - "replyCallback": { - "active": false - }, - "shouldStopOnFail": false, - "uuid": "3b512084-6cba-400c-8644-d9e9bff6bbfc", - "name": "Email", - "filters": [ - { - "children": [], - "_id": "6469fd9bc2c5b74546b2cc80" - } - ], - "_templateId": "6469fd9bc2c5b74546b2cc76", - "_parentId": null, - "metadata": { - "timed": { - "weekDays": [], - "monthDays": [] - } - }, - "_id": "6469fd9bc2c5b74546b2cc76", - "template": { - "_id": "6469fd9bc2c5b74546b2cc76", - "type": "email" - } - } - ], - "preferenceSettings": { - "email": true, - "sms": true, - "in_app": true, - "chat": true, - "push": true - }, - "_environmentId": "641eddefda8256b93f750e07", - "_organizationId": "641eddefda8256b93f750e01", - "_creatorId": "641eddee5d9af70da9f6a2e4", - "deleted": false, - "createdAt": "2023-05-21T11:12:07.036Z", - "updatedAt": "2023-05-21T11:40:12.955Z", - "__v": 0, - "notificationGroup": { - "_id": "641eddefda8256b93f750e0b", - "name": "General", - "_organizationId": "641eddefda8256b93f750e01", - "_environmentId": "641eddefda8256b93f750e07", - "createdAt": "2023-03-25T11:41:35.279Z", - "updatedAt": "2023-03-25T11:41:35.279Z", - "__v": 0 - }, - "workflowIntegrationStatus": { - "hasActiveIntegrations": true, - "channels": { - "in_app": { - "hasActiveIntegrations": true - }, - "email": { - "hasActiveIntegrations": true, - "hasPrimaryIntegrations": true - }, - "sms": { - "hasActiveIntegrations": true, - "hasPrimaryIntegrations": true - }, - "chat": { - "hasActiveIntegrations": true - }, - "push": { - "hasActiveIntegrations": false - } - } + "active": true, + "replyCallback": { + "active": false + }, + "shouldStopOnFail": false, + "uuid": "4e1d8ebb-355c-4224-8a79-fcbc3c0233a1", + "name": "Email", + "filters": [ + { + "children": [], + "_id": "64a420215f8a9af84368b6b8" + } + ], + "_templateId": "64a420215f8a9af84368b6a6", + "_parentId": "64a421ae5f8a9af84368c9fe", + "metadata": { + "timed": { + "weekDays": [], + "monthDays": [] + } + }, + "_id": "64a420215f8a9af84368b6a6", + "template": { + "_id": "64a420215f8a9af84368b6a6", + "type": "email" + } + } + ], + "preferenceSettings": { + "email": true, + "sms": true, + "in_app": true, + "chat": true, + "push": true + }, + "_environmentId": "641eddefda8256b93f750e07", + "_organizationId": "641eddefda8256b93f750e01", + "_creatorId": "641eddee5d9af70da9f6a2e4", + "deleted": false, + "createdAt": "2023-07-04T13:34:54.781Z", + "updatedAt": "2023-07-20T09:05:04.701Z", + "__v": 0, + "notificationGroup": { + "_id": "641eddefda8256b93f750e0b", + "name": "General", + "_organizationId": "641eddefda8256b93f750e01", + "_environmentId": "641eddefda8256b93f750e07", + "createdAt": "2023-03-25T11:41:35.279Z", + "updatedAt": "2023-03-25T11:41:35.279Z", + "__v": 0 + }, + "workflowIntegrationStatus": { + "hasActiveIntegrations": true, + "channels": { + "in_app": { + "hasActiveIntegrations": true + }, + "email": { + "hasActiveIntegrations": true, + "hasPrimaryIntegrations": true + }, + "sms": { + "hasActiveIntegrations": true, + "hasPrimaryIntegrations": true + }, + "chat": { + "hasActiveIntegrations": true + }, + "push": { + "hasActiveIntegrations": false + } + } + } + }, + { + "_id": "6492d92f616db8f05a982022", + "name": "digest showcase", + "active": true, + "draft": false, + "critical": true, + "isBlueprint": false, + "_notificationGroupId": "641eddefda8256b93f750e0b", + "tags": [], + "triggers": [ + { + "type": "event", + "identifier": "digest-showcase", + "variables": [], + "subscriberVariables": [], + "_id": "6492d92f616db8f05a982023" + } + ], + "steps": [ + { + "active": true, + "shouldStopOnFail": false, + "uuid": "556f0514-01b6-46f9-9e3d-72a4c060af54", + "name": "Digest", + "filters": [], + "_templateId": "6492d9735da9bfaf640771a2", + "_parentId": null, + "metadata": { + "amount": 10, + "unit": "seconds", + "type": "regular", + "backoffAmount": null, + "backoff": false, + "timed": { + "weekDays": [], + "monthDays": [] } + }, + "_id": "6492d9735da9bfaf640771a2", + "template": { + "_id": "6492d9735da9bfaf640771a2", + "type": "digest" + } }, { - "_id": "6450b033631887e5f013f0df", - "name": "notif-genenerator", - "active": true, - "draft": false, - "critical": false, - "isBlueprint": false, - "_notificationGroupId": "641eddefda8256b93f750e0b", - "tags": [], - "triggers": [ - { - "type": "event", - "identifier": "notif-genenerator", - "variables": [ - { - "name": "description", - "type": "String", - "_id": "645a5b18e1a93324256bdf59" - } - ], - "subscriberVariables": [], - "_id": "6450b033631887e5f013f0e0" - } - ], - "steps": [ - { - "active": true, - "shouldStopOnFail": false, - "uuid": "0486c699-2db4-4868-9ed5-c407acacb728", - "name": "In-App", - "filters": [ - { - "children": [], - "_id": "6450b06e9c851c56f803d489" - } - ], - "_templateId": "6450b06e9c851c56f803d47e", - "_parentId": null, - "_id": "6450b06e9c851c56f803d47e", - "template": { - "_id": "6450b06e9c851c56f803d47e", - "type": "in_app" - } - } - ], - "preferenceSettings": { - "email": true, - "sms": true, - "in_app": true, - "chat": true, - "push": true - }, - "_environmentId": "641eddefda8256b93f750e07", - "_organizationId": "641eddefda8256b93f750e01", - "_creatorId": "641eddee5d9af70da9f6a2e4", - "deleted": false, - "createdAt": "2023-05-02T06:39:47.699Z", - "updatedAt": "2023-05-09T14:39:20.811Z", - "__v": 0, - "notificationGroup": { - "_id": "641eddefda8256b93f750e0b", - "name": "General", - "_organizationId": "641eddefda8256b93f750e01", - "_environmentId": "641eddefda8256b93f750e07", - "createdAt": "2023-03-25T11:41:35.279Z", - "updatedAt": "2023-03-25T11:41:35.279Z", - "__v": 0 + "active": true, + "shouldStopOnFail": false, + "uuid": "024a27f4-82da-485c-b4a7-bea05c2631e3", + "name": "In-App", + "filters": [ + { + "children": [], + "_id": "6492d9735da9bfaf640771b6" + } + ], + "_templateId": "6492d9735da9bfaf640771aa", + "_parentId": "6492d9735da9bfaf640771a2", + "metadata": { + "timed": { + "weekDays": [], + "monthDays": [] + } + }, + "_id": "6492d9735da9bfaf640771aa", + "template": { + "_id": "6492d9735da9bfaf640771aa", + "type": "in_app" + } + } + ], + "preferenceSettings": { + "email": true, + "sms": true, + "in_app": true, + "chat": true, + "push": true + }, + "_environmentId": "641eddefda8256b93f750e07", + "_organizationId": "641eddefda8256b93f750e01", + "_creatorId": "641eddee5d9af70da9f6a2e4", + "deleted": false, + "createdAt": "2023-06-21T11:04:15.546Z", + "updatedAt": "2023-07-19T13:32:18.474Z", + "__v": 0, + "notificationGroup": { + "_id": "641eddefda8256b93f750e0b", + "name": "General", + "_organizationId": "641eddefda8256b93f750e01", + "_environmentId": "641eddefda8256b93f750e07", + "createdAt": "2023-03-25T11:41:35.279Z", + "updatedAt": "2023-03-25T11:41:35.279Z", + "__v": 0 + }, + "workflowIntegrationStatus": { + "hasActiveIntegrations": true, + "channels": { + "in_app": { + "hasActiveIntegrations": true + }, + "email": { + "hasActiveIntegrations": true, + "hasPrimaryIntegrations": true + }, + "sms": { + "hasActiveIntegrations": true, + "hasPrimaryIntegrations": true + }, + "chat": { + "hasActiveIntegrations": true + }, + "push": { + "hasActiveIntegrations": false + } + } + } + }, + { + "_id": "6471aa6f3a4ec8a599669991", + "name": "quickstart", + "active": true, + "draft": false, + "critical": false, + "isBlueprint": false, + "_notificationGroupId": "641eddefda8256b93f750e0b", + "tags": [], + "triggers": [ + { + "type": "event", + "identifier": "quickstart", + "variables": [ + { + "name": "description", + "type": "String", + "_id": "6471aaa08b0b51ed173ba8fd" + } + ], + "subscriberVariables": [], + "_id": "6471aa6f3a4ec8a599669992" + } + ], + "steps": [ + { + "active": true, + "shouldStopOnFail": false, + "uuid": "982bf6a1-6f0c-46c6-a8ad-6daa67f327e2", + "name": "In-App", + "filters": [ + { + "children": [], + "_id": "6471aa85720b54f89ed97de3" + } + ], + "_templateId": "6471aa85720b54f89ed97dd8", + "_parentId": null, + "metadata": { + "timed": { + "weekDays": [], + "monthDays": [] + } + }, + "_id": "6471aa85720b54f89ed97dd8", + "template": { + "_id": "6471aa85720b54f89ed97dd8", + "type": "in_app" + } + } + ], + "preferenceSettings": { + "email": true, + "sms": true, + "in_app": true, + "chat": true, + "push": true + }, + "_environmentId": "641eddefda8256b93f750e07", + "_organizationId": "641eddefda8256b93f750e01", + "_creatorId": "641eddee5d9af70da9f6a2e4", + "deleted": false, + "createdAt": "2023-05-27T06:59:59.254Z", + "updatedAt": "2023-05-27T07:00:48.618Z", + "__v": 0, + "notificationGroup": { + "_id": "641eddefda8256b93f750e0b", + "name": "General", + "_organizationId": "641eddefda8256b93f750e01", + "_environmentId": "641eddefda8256b93f750e07", + "createdAt": "2023-03-25T11:41:35.279Z", + "updatedAt": "2023-03-25T11:41:35.279Z", + "__v": 0 + }, + "workflowIntegrationStatus": { + "hasActiveIntegrations": true, + "channels": { + "in_app": { + "hasActiveIntegrations": true + }, + "email": { + "hasActiveIntegrations": true, + "hasPrimaryIntegrations": true + }, + "sms": { + "hasActiveIntegrations": true, + "hasPrimaryIntegrations": true + }, + "chat": { + "hasActiveIntegrations": true + }, + "push": { + "hasActiveIntegrations": false + } + } + } + }, + { + "_id": "6469fc87301d88d6d2b74741", + "name": "Email quickstart", + "active": true, + "draft": false, + "critical": false, + "isBlueprint": false, + "_notificationGroupId": "641eddefda8256b93f750e0b", + "tags": [], + "triggers": [ + { + "type": "event", + "identifier": "email-quickstart", + "variables": [ + { + "name": "email", + "type": "String", + "_id": "646a031c25b1fb30f274ac35" }, - "workflowIntegrationStatus": { - "hasActiveIntegrations": true, - "channels": { - "in_app": { - "hasActiveIntegrations": true - }, - "email": { - "hasActiveIntegrations": true, - "hasPrimaryIntegrations": true - }, - "sms": { - "hasActiveIntegrations": true, - "hasPrimaryIntegrations": true - }, - "chat": { - "hasActiveIntegrations": true - }, - "push": { - "hasActiveIntegrations": false - } - } + { + "name": "description", + "type": "String", + "_id": "646a031c25b1fb30f274ac36" + } + ], + "subscriberVariables": [ + { + "name": "email", + "_id": "646a031c25b1fb30f274ac34" + } + ], + "_id": "6469fc87301d88d6d2b74742" + } + ], + "steps": [ + { + "active": true, + "replyCallback": { + "active": false + }, + "shouldStopOnFail": false, + "uuid": "3b512084-6cba-400c-8644-d9e9bff6bbfc", + "name": "Email", + "filters": [ + { + "children": [], + "_id": "6469fd9bc2c5b74546b2cc80" + } + ], + "_templateId": "6469fd9bc2c5b74546b2cc76", + "_parentId": null, + "metadata": { + "timed": { + "weekDays": [], + "monthDays": [] + } + }, + "_id": "6469fd9bc2c5b74546b2cc76", + "template": { + "_id": "6469fd9bc2c5b74546b2cc76", + "type": "email" + } + } + ], + "preferenceSettings": { + "email": true, + "sms": true, + "in_app": true, + "chat": true, + "push": true + }, + "_environmentId": "641eddefda8256b93f750e07", + "_organizationId": "641eddefda8256b93f750e01", + "_creatorId": "641eddee5d9af70da9f6a2e4", + "deleted": false, + "createdAt": "2023-05-21T11:12:07.036Z", + "updatedAt": "2023-05-21T11:40:12.955Z", + "__v": 0, + "notificationGroup": { + "_id": "641eddefda8256b93f750e0b", + "name": "General", + "_organizationId": "641eddefda8256b93f750e01", + "_environmentId": "641eddefda8256b93f750e07", + "createdAt": "2023-03-25T11:41:35.279Z", + "updatedAt": "2023-03-25T11:41:35.279Z", + "__v": 0 + }, + "workflowIntegrationStatus": { + "hasActiveIntegrations": true, + "channels": { + "in_app": { + "hasActiveIntegrations": true + }, + "email": { + "hasActiveIntegrations": true, + "hasPrimaryIntegrations": true + }, + "sms": { + "hasActiveIntegrations": true, + "hasPrimaryIntegrations": true + }, + "chat": { + "hasActiveIntegrations": true + }, + "push": { + "hasActiveIntegrations": false + } + } + } + }, + { + "_id": "6450b033631887e5f013f0df", + "name": "notif-genenerator", + "active": true, + "draft": false, + "critical": false, + "isBlueprint": false, + "_notificationGroupId": "641eddefda8256b93f750e0b", + "tags": [], + "triggers": [ + { + "type": "event", + "identifier": "notif-genenerator", + "variables": [ + { + "name": "description", + "type": "String", + "_id": "645a5b18e1a93324256bdf59" } + ], + "subscriberVariables": [], + "_id": "6450b033631887e5f013f0e0" } - ], - "totalCount": 20, - "pageSize": 10 + ], + "steps": [ + { + "active": true, + "shouldStopOnFail": false, + "uuid": "0486c699-2db4-4868-9ed5-c407acacb728", + "name": "In-App", + "filters": [ + { + "children": [], + "_id": "6450b06e9c851c56f803d489" + } + ], + "_templateId": "6450b06e9c851c56f803d47e", + "_parentId": null, + "_id": "6450b06e9c851c56f803d47e", + "template": { + "_id": "6450b06e9c851c56f803d47e", + "type": "in_app" + } + } + ], + "preferenceSettings": { + "email": true, + "sms": true, + "in_app": true, + "chat": true, + "push": true + }, + "_environmentId": "641eddefda8256b93f750e07", + "_organizationId": "641eddefda8256b93f750e01", + "_creatorId": "641eddee5d9af70da9f6a2e4", + "deleted": false, + "createdAt": "2023-05-02T06:39:47.699Z", + "updatedAt": "2023-05-09T14:39:20.811Z", + "__v": 0, + "notificationGroup": { + "_id": "641eddefda8256b93f750e0b", + "name": "General", + "_organizationId": "641eddefda8256b93f750e01", + "_environmentId": "641eddefda8256b93f750e07", + "createdAt": "2023-03-25T11:41:35.279Z", + "updatedAt": "2023-03-25T11:41:35.279Z", + "__v": 0 + }, + "workflowIntegrationStatus": { + "hasActiveIntegrations": true, + "channels": { + "in_app": { + "hasActiveIntegrations": true + }, + "email": { + "hasActiveIntegrations": true, + "hasPrimaryIntegrations": true + }, + "sms": { + "hasActiveIntegrations": true, + "hasPrimaryIntegrations": true + }, + "chat": { + "hasActiveIntegrations": true + }, + "push": { + "hasActiveIntegrations": false + } + } + } + } + ], + "totalCount": 20, + "pageSize": 10 } ``` - \ No newline at end of file + diff --git a/api-reference/workflows/update-workflow-status.mdx b/api-reference/workflows/update-workflow-status.mdx index 6d1131fa..448d748f 100644 --- a/api-reference/workflows/update-workflow-status.mdx +++ b/api-reference/workflows/update-workflow-status.mdx @@ -19,11 +19,16 @@ import ApikeyWarning from "/snippets/apikey-warning.mdx"; ``` ```javascript Node.js -import { Novu, TemplateVariableTypeEnum, FilterPartTypeEnum, StepTypeEnum } from '@novu/node'; +import { + Novu, + TemplateVariableTypeEnum, + FilterPartTypeEnum, + StepTypeEnum, +} from "@novu/node"; -const novu = new Novu(''); +const novu = new Novu(""); -await novu.notificationTemplates.updateStatus("workflowId", false) +await novu.notificationTemplates.updateStatus("workflowId", false); ``` ```php PHP @@ -90,7 +95,7 @@ func main() { body, _ := ioutil.ReadAll(resp.Body) fmt.Println(string(body)) } - + ``` ```kotlin Kotlin @@ -127,9 +132,7 @@ class Main { "push": true }, "critical": true, - "tags": [ - "string" - ], + "tags": ["string"], "steps": [ { "_id": "string", @@ -205,4 +208,4 @@ class Main { } ``` - \ No newline at end of file + diff --git a/api-reference/workflows/update-workflow.mdx b/api-reference/workflows/update-workflow.mdx index 65653fc8..0ede7390 100644 --- a/api-reference/workflows/update-workflow.mdx +++ b/api-reference/workflows/update-workflow.mdx @@ -19,13 +19,18 @@ import ApikeyWarning from "/snippets/apikey-warning.mdx"; ``` ```javascript Node.js -import { Novu, TemplateVariableTypeEnum, FilterPartTypeEnum, StepTypeEnum } from '@novu/node'; +import { + Novu, + TemplateVariableTypeEnum, + FilterPartTypeEnum, + StepTypeEnum, +} from "@novu/node"; -const novu = new Novu(''); +const novu = new Novu(""); await novu.notificationTemplates.update("workflowId", { name: "Send daily digest email update", - description: "This workflow will send daily digest email to user at 9:00 AM" + description: "This workflow will send daily digest email to user at 9:00 AM", /** * all other fields from create workflow payload */ @@ -62,7 +67,7 @@ body = { 'description' => 'description', # optional 'steps' => [ # optional # insert all fields here - ], + ], 'active' => true, # optional 'draft' => true, # optional 'critical' => true, # optional @@ -128,7 +133,7 @@ func main() { body, _ := ioutil.ReadAll(resp.Body) fmt.Println(string(body)) } - + ``` ```kotlin Kotlin @@ -165,9 +170,7 @@ class Main { "push": true }, "critical": true, - "tags": [ - "string" - ], + "tags": ["string"], "steps": [ { "_id": "string", @@ -243,4 +246,4 @@ class Main { } ``` - \ No newline at end of file + diff --git a/architecture/diagrams.mdx b/architecture/diagrams.mdx index b2508538..7a3a0e23 100644 --- a/architecture/diagrams.mdx +++ b/architecture/diagrams.mdx @@ -2,13 +2,14 @@ title: "Novu Architecture Diagram" version: "v0.21.0" --- + # Novu Architecture Diagram powered by [terrastruct](https://terrastruct.com) and D2 If the diagram below is not rendering, please [click here](https://app.terrastruct.com/diagrams/1895866270) to view it on terrastruct.com. + title="Novu" + width="1280" + height="720" + src="https://app.terrastruct.com/diagrams/745235552" +> diff --git a/architecture/introduction.mdx b/architecture/introduction.mdx index f8e9f4df..b1c5e62c 100644 --- a/architecture/introduction.mdx +++ b/architecture/introduction.mdx @@ -3,6 +3,7 @@ title: "Introduction to Novu" description: "Overview of Novu's Architecture" version: "v0.21.0" --- + Novu is an open-source notification infrastructure designed to provide a robust, efficient, and flexible solution for managing notifications across various platforms. The architecture of Novu is meticulously crafted to ensure accurate delivery of notifications while maintaining high performance and scalability. diff --git a/channels-and-providers/default-providers.mdx b/channels-and-providers/default-providers.mdx deleted file mode 100644 index 02d1a2d9..00000000 --- a/channels-and-providers/default-providers.mdx +++ /dev/null @@ -1,44 +0,0 @@ ---- -title: 'Novu Providers' -description: 'Learn about the default Novu email and sms providers' -icon: 'code' ---- - -import CloudOnlyFeature from "/snippets/cloud-only-feature.mdx" - -## Novu Providers - -To help you evaluate our services better, Novu provides an email and sms provider by default for every account. After signing up, you can go to the [Integrations store](https://web.novu.co/integrations?utm_campaign=docs-default-providers) on the Novu web dashboard to see this. - - - - -## Novu Email Provider -If you've a newly signed-up account on Novu, it will look like this: - - - -## Novu SMS Provider -And this is what the default Novu SMS provider looks like: - - - - -The default email and sms providers are for evaluation purposes only and their use in production-grade apps is not recommended. You should switch to any of our [numerous other providers](/additional-resources/glossary#3-providers) for production apps. Also, there are two modes for default Novu providers - Development and Production. - - -## Novu In-app Provider -In addition to email and sms, we also have in-app provider: - - - -It is the only provider for the in-app channel and it *can be used in production apps*. Unlike sms and email, it is not active by default and needs to be turned on separately. It can be scaled as per your need and it is a 'pay as you go' offering. You can check [this](https://novu.co/pricing/?utm_campaign=docs-default-providers) for more details. - - -## Limits of the Novu Providers -Following are the limits for our email and sms providers: - -1. Email: 300 emails per organization per month -2. SMS: 20 messages per organization per month - -To send more than these limits, you can configure a different provider for a specific channel. Read more about them [here](/channels-and-providers/integration-store). diff --git a/channels-and-providers/integration-store.mdx b/channels-and-providers/integration-store.mdx deleted file mode 100644 index 37421bef..00000000 --- a/channels-and-providers/integration-store.mdx +++ /dev/null @@ -1,30 +0,0 @@ ---- -title: 'Integration Store' -description: "Learn about Novu's integration store" -icon: 'store' ---- - -## Introduction -Novu's integration store serves as the central hub from where you can seamlessly enable and disable various services for efficient communication. - -This feature simplifies the process of managing your communication channels. You can see which provider is enabled/disabled for what channel, which provider works in what environment and more. Moreover, it provides a clear overview of enabled and disabled services, offering you greater control over your communication strategy. - - - -From this image above, you can see that I've configured `Sendgrid` and `Twilio` as my email and sms provider respectively and both are in the `development` environment. - -## Streamline Communication Services - -With the Integration Store, you can streamline your communication services effortlessly. Whether it's Email, Chat, or SMS, this feature allows you to integrate different communication channels with ease. This centralization ensures that you can manage all your services conveniently from a single location. - -## Enabling and Disabling Services - -The Integration Store allows you to activate or deactivate specific services according to your communication needs. You can enable a service by simply entering the required `API key`, `identifier` and other required info depending on the provider you're choosing. Conversely, if you need to deactivate a service temporarily or make changes, you can disable it just as effortlessly. - -## Enhanced Flexibility - -By providing a comprehensive range of communication services under one roof, the Integration Store enhances your flexibility. You have the power to customize your communication strategy by enabling the services that align with your requirements. - -## Conclusion - -Novu's integration store offers a centralized and user-friendly platform for managing your communication services effectively. With its categorization, clear visibility, and easy enablement/disabling of services, you can fine-tune your communication strategy and streamline your interactions, enhancing the overall communication experience of your product. \ No newline at end of file diff --git a/channels-and-providers/introduction.mdx b/channels-and-providers/introduction.mdx deleted file mode 100644 index 782abc89..00000000 --- a/channels-and-providers/introduction.mdx +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: 'Overview' -description: 'Learn about what channels, providers and oganizations are' -icon: 'code' ---- - -## Channels: - -Novu lets you send notifications across different communication mediums, including emails, in-app messages, push notifications, SMS, and chat. Each of these five communication mediums is referred to as a notification ‘channel’. - - - -## Providers: - -Providers are what allow us to handle message delivery over multiple channels. Of the five notification channels that Novu currently supports, each channel except the ‘in-app’ channel has multiple providers. - -The in-app channel only has one provider (the default Novu provider). - -All the remaining channels have multiple providers. - -## Organizations: -Organizations enable you to segregate notifications across multiple products that you may have. You can use Novu to send notifications across multiple apps and can create organizations. - - - -Credentials are saved per instance of the active integrations of a provider. In case of more than one active provider of the same type, If `integrationIdentifier` is not sent in [this](https://docs.novu.co/api-reference/subscribers/update-subscriber-credentials) API, then the most recently created provider will be used to update the credentials. \ No newline at end of file diff --git a/community/add-a-new-provider.mdx b/community/add-a-new-provider.mdx index f5d620fe..fc7c616d 100644 --- a/community/add-a-new-provider.mdx +++ b/community/add-a-new-provider.mdx @@ -8,7 +8,7 @@ Interested in expanding Novu's capabilities? By contributing to our growing ecos ## How to add a new provider? -Novu currently supports five channels `in_app`, `push`, `email`, `chat` and `sms`. +Novu currently supports five channels `in_app`, `push`, `email`, `chat` and `sms`. For `in_app` we support only our own provider, so new providers cannot be added to this channel. For the other four channels, we support the integration of external providers. This guide will help in adding new providers for any of these 4 channels. @@ -16,7 +16,7 @@ In this guide, we are adding a new provider for the email channel, but all of th ## Description -Providers allow us to handle message delivery over multiple channels. +Providers allow us to handle message delivery over multiple channels. We have multiple providers for each channel (SMS, Email, Push and Chat). To get started with adding a new provider let's look at setting up our repository. @@ -30,7 +30,10 @@ We have multiple providers for each channel (SMS, Email, Push and Chat). To get Need help installing the requirements? [Read more here](/community/run-in-local-machine) - We have used pnpm package manager in this guide. You can use npm as well. + + {" "} + We have used pnpm package manager in this guide. You can use npm as well.{" "} + ## Initialization @@ -116,16 +119,23 @@ export class ExampleProviderEmailProvider implements IEmailProvider { ### Template test case for `example-provider`. ```ts packages/providers/src/lib/example-provider/example-provider.provider.sepc.ts -import { ExampleProviderEmailProvider } from './example-provider.provider'; +import { ExampleProviderEmailProvider } from "./example-provider.provider"; -test('should trigger example-provider library correctly', async () => {}); +test("should trigger example-provider library correctly", async () => {}); ``` - Add the provider's SDK as a dependency in the provider's package.json file. Run `pnpm run setup:project` to build all dependencies again. Use this new sdk method to complete the provider's `sendMessage` function. Check the **[reference links for adding new providers](#reference-for-adding-new-providers)** section for each channel's provider example. + + {" "} + Add the provider's SDK as a dependency in the provider's package.json file. Run + `pnpm run setup:project` to build all dependencies again. Use this new sdk method + to complete the provider's `sendMessage` function. Check the **[reference links + for adding new providers](#reference-for-adding-new-providers)** section for each + channel's provider example.{" "} + ### Add provider logos -In order to present this new provider in the `integration store` we need logos in dark and light mode. Add dark color svg logo in  `apps/web/public/static/images/providers/dark/sqaure` directory and light color svg logo in  `apps/web/public/static/images/providers/light/sqaure` directory. +In order to present this new provider in the `integration store` we need logos in dark and light mode. Add dark color svg logo in  `apps/web/public/static/images/providers/dark/sqaure` directory and light color svg logo in  `apps/web/public/static/images/providers/light/sqaure` directory. Use the provider name as the file name. The sample name for our above-added provider is `example-provider.svg`. @@ -157,6 +167,7 @@ export const exampleProviderConfig: IConfigCredentials[] = [ 1. Here the `key` is of type `CredentialsKeyEnum`. > If a new key is added, add this key at these 3 places:- +> > - In `CredentialsKeyEnum` at file `libs/shared/src/consts/providers/provider.enum.ts` > - In `CredentialsDto` at file `apps/api/src/app/integrations/dtos/credentials.dto.ts` > - In `credentials` field of `integrationSchema` at file `libs/dal/src/repositories/integration/integration.schema.ts` @@ -267,7 +278,13 @@ If everything is working fine without any error, commit your local branch change Hurray 🎉! You have successfully added a new provider in Novu! -️ In this guide, we have used only one credential `apiKey` for our `example-provider`. This is for reference purposes only. A provider can have more than one credential as per its `SDK` requirements. At each step, you will need to add all credentials carefully. Check providers referenced below for more information. + + ️ In this guide, we have used only one credential `apiKey` for + our `example-provider`. This is for reference purposes only. A provider can + have more than one credential as per its `SDK` requirements. At each step, you + will need to add all credentials carefully. Check providers referenced below + for more information.{" "} + ### Reference for Adding New Providers diff --git a/community/changelog.mdx b/community/changelog.mdx index 8e61fd37..95d9a7d6 100644 --- a/community/changelog.mdx +++ b/community/changelog.mdx @@ -3,21 +3,34 @@ title: "Changelog" description: "See the most recent changes and learn about how to shape Novu's future" --- - - Learn about what's changed, new features, bug fixes, and see Novu's history across versions. + + Learn about what's changed, new features, bug fixes, and see Novu's history + across versions. Using the changelog, you can: + - Keep an eye on the latest updates, - Learn more about the newly added features and improvements, and - Stay informed about bug fixes and enhancement ## Getting involved: + Community is at the heart of everything we do at Novu. To get more involved with the community, you can: + - **Fork and Contribute** to our open issues as well as suggest new ideas at our [github repository](https://github.com/novuhq/novu) - **Join our community** to ask questions, engage with other users and share ideas. Here's the [joining link.](https://discord.gg/novu?ref=docs-join-our-community) - **Participate in our office hours** to learn more and connect with our core team. Join us [here.](https://www.youtube.com/@novuhq/streams) -Remember, we're not building just another product, but a community of passionate developers who shape its evolution. Our changelog isn't just a list of updates but a reflection of our journey together. Your voice matters, and your ideas and feedback are what fuel our progress and shape our future. So, join us in this adventure, and – Let's build something amazing, one feature at a time! - \ No newline at end of file + Remember, we're not building just another product, but a community of + passionate developers who shape its evolution. Our changelog isn't just a list + of updates but a reflection of our journey together. Your voice matters, and + your ideas and feedback are what fuel our progress and shape our future. So, + join us in this adventure, and – Let's build something amazing, one feature at + a time! + diff --git a/community/code-of-conduct.mdx b/community/code-of-conduct.mdx index 99b728f9..00fcd75f 100644 --- a/community/code-of-conduct.mdx +++ b/community/code-of-conduct.mdx @@ -4,7 +4,6 @@ sidebarTitle: "Code of Conduct" description: "The set of rules and guidelines that govern interaction among community members" --- - As a community-driven company, Novu is committed to creating an inclusive and welcoming environment for all members, regardless of factors such as age, body size, disability, ethnicity, gender identity, experience level, education, socio-economic status, nationality, personal appearance, race, religion, or sexual orientation. However, diverse communities may face challenges, such as potential misunderstandings and miscommunications. To ensure respectful interactions, free from behaviour that may create an unsafe environment, we have established this Code of Conduct. @@ -27,7 +26,6 @@ It's crucial to keep in mind that our community members are from all kinds of ba - Accept responsibility and apologise to those affected by mistakes, and learn from such experiences - Focus on what is best not just for us as individuals, but for the overall community! - ### Patience Our community thrives on the generosity of volunteered time. Questions, contributions, and support requests may embark on a time-travelling journey before finding their destination. Repeated "bumps" or persistent "reminders" don’t display patience and are looked down upon. Lastly, it is a bad practice to ask general questions to a specific person (in direct messages for example). Try to ask in public as much as you can, and patiently wait for the response @@ -36,7 +34,7 @@ Our community thrives on the generosity of volunteered time. Questions, contribu Please be courteous and respectful to fellow members. Avoid offensive comments related to age, body size, disability, ethnicity, gender identity, experience level, education, socio-economic status, nationality, personal appearance, race, religion, or sexual orientation. -Strictly prohibited are sexualized imagery, violence, intimidation, stalking, disruptions, sharing personal information without explicit permission, unwanted physical contact, and unwelcome sexual attention. +Strictly prohibited are sexualized imagery, violence, intimidation, stalking, disruptions, sharing personal information without explicit permission, unwanted physical contact, and unwelcome sexual attention. Use inclusive language respecting our community's diversity. @@ -46,9 +44,9 @@ Avoid assumptions about others' backgrounds. Maintain a positive and professiona ### Inquisitive -***The only stupid question is the one that does not get asked***. +**_The only stupid question is the one that does not get asked_**. -We encourage our users to ask early and ask often. Rather than asking whether you can ask a question (the answer is always yes!), instead, simply ask your question. You are encouraged to provide as many specifics as possible. +We encourage our users to ask early and ask often. Rather than asking whether you can ask a question (the answer is always yes!), instead, simply ask your question. You are encouraged to provide as many specifics as possible. Code snippets in the form of images are bad practice. Instead, use text formatted as code (using backticks) on Discord or simply send a gist. Refrain from pasting multiple lines of code directly into the chat channels - instead use [gist.github.com](http://gist.github.com/) or another paste site to provide code snippets. @@ -91,15 +89,22 @@ We expect all participants, organizers, speakers, and attendees to follow these Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: 1. **Correction** + - `Community Impact`: Use of inappropriate language or other behaviour deemed unprofessional or unwelcome in the community. - `Consequence`: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behaviour was inappropriate. A public apology may be requested. + 2. **Warning** + - `Community Impact`: A violation through a single incident or series of actions. - `Consequence`: A warning with consequences for continued behaviour. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. + 3. **Temporary Ban** + - `Community Impact`: A serious violation of community standards, including sustained inappropriate behaviour. - `Consequence`: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. + 4. **Permanent Ban** + - `Community Impact`: Demonstrating a pattern of violation of community standards, including sustained inappropriate behaviour, harassment of an individual, or aggression toward or disparagement of classes of individuals. - `Consequence`: A permanent ban from any sort of public interaction within the community. @@ -117,6 +122,6 @@ Our Code of Conduct was adapted from Codes of Conduct of other open-source proje Community Impact Guidelines were inspired by Mozilla's code of conduct enforcement ladder. -For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. +For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. -Translations are available at https://www.contributor-covenant.org/translations. \ No newline at end of file +Translations are available at https://www.contributor-covenant.org/translations. diff --git a/self-hosting-novu/deploy-with-docker.mdx b/community/deploy-with-docker.mdx similarity index 77% rename from self-hosting-novu/deploy-with-docker.mdx rename to community/deploy-with-docker.mdx index bbb283d9..17c176f9 100644 --- a/self-hosting-novu/deploy-with-docker.mdx +++ b/community/deploy-with-docker.mdx @@ -1,7 +1,7 @@ --- -title: 'Deploy with Docker' -description: 'Learn how to deploy Novu with Docker' -icon: 'docker' +title: "Deploy with Docker" +description: "Learn how to deploy Novu with Docker" +icon: "docker" --- Docker compose is the easiest way to get started with self-hosted Novu. @@ -13,10 +13,18 @@ You need the following installed in your system: - [Docker](https://docs.docker.com/engine/install/) and [docker-compose](https://docs.docker.com/compose/install/) - [Git](https://git-scm.com/downloads) + - - -In above loom video `local/deployment` directory is used. Now it has been moved to `docker/community`. Follow below mentioned steps. + + In above loom video `local/deployment` directory is used. Now it has been + moved to `docker/community`. Follow below mentioned steps. + ## Quick Start @@ -44,7 +52,7 @@ Now visit [http://localhost:4200](http://localhost:4200/) to start using Novu. While we provide you with some example secrets for getting started, you should NEVER deploy your Novu setup using the defaults provided. -Update the `.env` file with your own secrets. +Update the `.env` file with your own secrets. ### Required Variables: @@ -68,17 +76,17 @@ We strongly recommend that you decouple your database before deploying. When self-hosting Novu, in order to trigger an event you must first create a new `Novu` object and configure it with the proper `backendUrl`. ```tsx -import { Novu } from '@novu/node'; +import { Novu } from "@novu/node"; const config = { - backendUrl: '', + backendUrl: "", }; -const novu = new Novu('', config); +const novu = new Novu("", config); -await novu.trigger('', { +await novu.trigger("", { to: { - subscriberId: '', + subscriberId: "", }, payload: {}, }); @@ -91,17 +99,16 @@ When using the IFrame embed to attach the notification center rather than the Re ```html - ``` ### Using React Component with custom installation @@ -121,8 +128,7 @@ Currently, we are caching data in the most heavily loaded areas of the system: t To implement a reverse-proxy or load balancer in front of Novu, you need to set the GLOBAL_CONTEXT_PATH for the base path of the application. This is the path that the application will be served from after the domain. -For example: - - company.com/novu +For example: - company.com/novu This is used to set the base path for the application, and is used to set the base path for the API, web, and websocket connections. The following environment variables are used to set the context path for each public service that Novu provides: @@ -140,17 +146,15 @@ the WIDGET_CONTEXT_PATH to widget, the WS_CONTEXT_PATH to ws, and the WEB_CONTEXT_PATH to web. -This would produce the following urls: - - API: company.com/novu/api - - WIDGET: company.com/novu/widget - - WS: company.com/novu/ws - - WEB: company.com/novu/web +This would produce the following urls: - API: company.com/novu/api - WIDGET: company.com/novu/widget - WS: company.com/novu/ws - WEB: company.com/novu/web However the Service context path can be used entirely independently of the GLOBAL_CONTEXT_PATH. For example, if I wanted to expose the api as novu-api, I would set the API_CONTEXT_PATH to novu-api without setting the GLOBAL_CONTEXT_PATH. -This would producte the following url: - - API: company.com/novu-api - - These env variables should be present on all services novu provides due to tight coupling. +This would producte the following url: - API: company.com/novu-api + + {" "} + These env variables should be present on all services novu provides due to tight + coupling. + diff --git a/community/feature-flags.mdx b/community/feature-flags.mdx index e8b4834a..08a70acb 100644 --- a/community/feature-flags.mdx +++ b/community/feature-flags.mdx @@ -8,14 +8,22 @@ Feature flags allow to turn certain functionality on and off based on configurat To enable the specific feature, you need to pass an environment variable to all services: - Feature flag environment variable accepts boolean values: `true` or `false`. + Feature flag environment variable accepts boolean values: `true` or `false`. - `IS_MULTI_PROVIDER_CONFIGURATION_ENABLED` adds ability to connect multiple providers per channel and make them active. It also shows redesigned integrations store page. - + + + {" "} + - `IS_MULTI_TENANCY_ENABLED` adds ability to manage tenants from dashboard. - + + + {" "} + - `IS_TEMPLATE_STORE_ENABLED` enables template store which contains pre-made workflows for common use cases (like **Password Reset** workflow etc). - + + {" "} + diff --git a/community/get-involved.mdx b/community/get-involved.mdx index f767a939..fdc35481 100644 --- a/community/get-involved.mdx +++ b/community/get-involved.mdx @@ -25,8 +25,10 @@ Our fellow community members are always ready to help you get past a blocker. Ho - If there is any issue in our backend SDKs, please mention the SDK version and relevant details. - If looking for self-hosting support, please share `Novu Version` and remote server details. Our latest version is `0.24.0`. Please be patient with self-hosting help. We are a small team and we will try our best to help you. -Intercome is suitable if you're using Novu cloud or. Kindly use Discord or Github for community self-hosting questions. - + + Intercome is suitable if you're using Novu cloud or. Kindly use Discord or + Github for community self-hosting questions. + ## Contributing @@ -41,19 +43,19 @@ All community members are of one of the following four types: - Everyone who joins our community becomes an open-source user automatically. - You can become an active community member by: - - Submitting >2 PRs - - Opening >2 issues - - Sending >5 messages on Github - - Commenting >3 times on Github + - Submitting >2 PRs + - Opening >2 issues + - Sending >5 messages on Github + - Commenting >3 times on Github - Active community members are one step closer to becoming Power community members and then an active moderator. ### Become a power community member - All active community members are eligible to become a power community member and can become so by having at least one of the following: - - Submitting 3 PRs - - Opening 3 Issues - - Sending 10 Discord messages - - Commenting >10 times on Github + - Submitting 3 PRs + - Opening 3 Issues + - Sending 10 Discord messages + - Commenting >10 times on Github - Power community members are just one step away from becoming a Novu ambassador. ### Become a Novu Ambassador @@ -68,7 +70,6 @@ A Novu ambassador is a trusted power member of the community that can do the fol We are launching the Novu Ambassador program soon with amazing perks. Stay tuned! - ## Activities Are you passionate about notifications like we are? There are many different—and easy—ways to get involved. @@ -84,16 +85,15 @@ Are you passionate about notifications like we are? There are many different—a - Check out our[GitHub repo](https://github.com/novuhq/docs) - No documentation is perfect, and neither is ours - Help us improve our docs by - - Updating outdated examples - - Correcting typos and language for clarity - - Find and fix broken links, etc. - + - Updating outdated examples + - Correcting typos and language for clarity + - Find and fix broken links, etc. ### Help with the SDKs - Link to contributors guide(https://github.com/novuhq/novu/blob/next/CONTRIBUTING.md) - We have SDKs in various languages and frameworks, most written and maintained by community members. -- You can either become a maintainer there or help the existing maintainers by bringing the SDK up to speed with the latest features present in the core product. +- You can either become a maintainer there or help the existing maintainers by bringing the SDK up to speed with the latest features present in the core product. - We have backend SDKs in the following: [Node.js](https://github.com/novuhq/novu/tree/next/packages/node), [PHP](https://github.com/novuhq/novu-php), [.NET](https://github.com/novuhq/novu-dotnet), [Elixir](https://github.com/novuhq/novu-elixir), [Go](https://github.com/novuhq/go-novu), [Ruby](https://github.com/novuhq/novu-ruby), [Python](https://github.com/novuhq/novu-python) [Laravel](https://github.com/novuhq/novu-laravel), and [Kotlin](https://github.com/novuhq/novu-kotlin) - And client-side SDKs in the follwing: [Vue](https://github.com/novuhq/novu/tree/next/packages/notification-center-vue), [React](https://github.com/novuhq/novu/tree/next/packages/notification-center), [Angular](https://github.com/novuhq/novu/tree/next/packages/notification-center-angular), [Web Component](https://github.com/novuhq/novu/tree/next/packages/notification-center), [iframe embed](https://docs.novu.co/notification-center/client/iframe), and [Headless Javascript.](https://docs.novu.co/notification-center/client/headless/get-started) @@ -111,7 +111,9 @@ Are you passionate about notifications like we are? There are many different—a - Or a feature request if you find something that should be a feature but isn't. -At Novu, we believe that no contribution is small, and the only wrong question is the one that doesn’t get asked. So feel free to ask any question or raise that Pull Request. You’re always welcome here! 🤗 + At Novu, we believe that no contribution is small, and the only wrong question + is the one that doesn’t get asked. So feel free to ask any question or raise + that Pull Request. You’re always welcome here! 🤗 ### Participate in office hours @@ -121,4 +123,4 @@ At Novu, we believe that no contribution is small, and the only wrong question i We’re excited to have you on board and look forward to your valuable contributions! -Together, we’ll shape the future of Novu. 🫂 \ No newline at end of file +Together, we’ll shape the future of Novu. 🫂 diff --git a/community/introduction.mdx b/community/introduction.mdx index 1c01312a..39be2634 100644 --- a/community/introduction.mdx +++ b/community/introduction.mdx @@ -15,7 +15,7 @@ How to get help and support from the Novu team? - If there is any bug, please share steps to reproduce that bug. - If there is any issue in running Novu in local machine, please share system details like operating system, RAM size, npm version, node version. - If there is any issue in our backend sdks, please mention sdk version and relevant details. -- If looking for self hosting support, please share `Novu Version` and remote server details. Our latest version is `0.21.0`. Please be patient for self hosting help. We are a small team and we will try our best to help you. +- If looking for self hosting support, please share `Novu Version` and remote server details. Our latest version is `0.21.0`. Please be patient for self hosting help. We are a small team and we will try our best to help you. ### Discord @@ -27,7 +27,10 @@ Join us on [Discord](https://discord.gg/novu?ref=docs-community-introduction) to For personalized support, our Intercom chat is available directly within the Novu app. Our team is ready to answer your queries and provide solutions to ensure your experience with Novu is smooth and productive. -Intercom is best for issues in using Novu cloud option only. Kindly use discord or github for community self hosting questions. + + Intercom is best for issues in using Novu cloud option only. Kindly use + discord or github for community self hosting questions. + ### GitHub Issues diff --git a/community/machine-setup.mdx b/community/machine-setup.mdx index c4096d9e..d7750a81 100644 --- a/community/machine-setup.mdx +++ b/community/machine-setup.mdx @@ -131,7 +131,7 @@ Follow the instructions to install the AWS CLI so you can use it to create env l You should get something like this: - AWS CLI + AWS CLI If needed, see: @@ -256,5 +256,5 @@ For better productivity and wellness, we suggest removing the not-used apps from Right Click on the app icon > Options > Remove from Dock - Dock + Dock diff --git a/community/monorepo-structure.mdx b/community/monorepo-structure.mdx index 95646785..c6d7e3bb 100644 --- a/community/monorepo-structure.mdx +++ b/community/monorepo-structure.mdx @@ -27,7 +27,6 @@ For additional information on running Novu locally, visit the [run locally](http The `apps` folder contains high-level applications and APIs. The app's outputs usually contain deployable units that a user can interact with either as an API or as a web/cli application. - The API package is our main service for handling backend logic. It handles anything from authentication, authorization, workflow management, triggering events, etc... This is where the Novu business logic is handled. @@ -39,9 +38,10 @@ The `apps` folder contains high-level applications and APIs. The app's outputs u This is the Novu admin panel which is used to visually communicate with the API. You can configure workflows, manage content, enable or disable notifications, visually track the notification activity feed, etc... The `WEB` project is a create-react-app built, well, with React. 😄 + - This is the client of our embeddable notification center widget. It is consumed mainly with the embed script in an Iframe. We can access it on port 4500 to interact with it directly. + This is the client of our embeddable Inbox widget. It is consumed mainly with the embed script in an Iframe. We can access it on port 4500 to interact with it directly. This is our service for managing and handling workers. @@ -64,21 +64,27 @@ The `apps` folder contains high-level applications and APIs. The app's outputs u This is the connector between our client's web app and the widget project. It’s a small shim script that generates an iframe and attaches it to a client-specified div to host the notification widget. If you are familiar with the Google Analytics embedded snippet or intercom-like embeddings, it uses the same mechanics. + - ## Packages (on npm) - A Standalone Node.js wrapper around the Novu API. Exists to provide type-safe and easier access to the different API endpoints Novu exposes (Triggers, subscribers, etc…). + A Standalone Node.js wrapper around the Novu API. Exists to provide + type-safe and easier access to the different API endpoints Novu exposes + (Triggers, subscribers, etc…). - A Nest.js wrapper around the `@novu/node` package was created by the community to easily interact with the core library from a nest project. Also released on NPM as a package. + A Nest.js wrapper around the `@novu/node` package was created by the + community to easily interact with the core library from a nest project. Also + released on NPM as a package. - React component library that contains widget bell with the notification center. Can get override of components like ‘bell icon’, and ‘notification center’. + React component library that contains widget bell with the notification + center. Can get override of components like ‘bell icon’, and ‘notification + center’. @@ -124,4 +130,4 @@ Novu provides a single API to manage providers across multiple channels with a s ### 📱 In-App -- [Novu](https://docs.novu.co/notification-center/getting-started) \ No newline at end of file +- [Novu](https://docs.novu.co/notification-center/getting-started) diff --git a/community/roadmap.mdx b/community/roadmap.mdx index 6d3cc9b2..a49b89f0 100644 --- a/community/roadmap.mdx +++ b/community/roadmap.mdx @@ -7,8 +7,7 @@ description: "Learn about our roadmap" Learn about ongoing developments, upcoming plans, and items in our backlog.. - -Your involvement in shaping the future of our notification infrastructure solution is highly encouraged. +Your involvement in shaping the future of our notification infrastructure solution is highly encouraged. ## Get involved @@ -16,6 +15,11 @@ Your involvement in shaping the future of our notification infrastructure soluti **Upvote and Comment:** Review the existing feature requests and upvote the ones that resonate with you. Feel free to provide additional insights or use-cases through comments. -**Contribute:** If you're a developer, you can actively contribute to the development of the features in progress or even the backlog ones. [Join our community of contributors](https://discord.gg/novu?ref=docs-contribute) and help us bring these enhancements to life. +**Contribute:** If you're a developer, you can actively contribute to the development of the features in progress or even the backlog ones. [Join our community of contributors](https://discord.gg/novu?ref=docs-contribute) and help us bring these enhancements to life. -Remember, this roadmap is a living document that evolves based on your input and the direction the community decides to take. Your voice matters, and we're excited to work together in building a robust notification infrastructure solution that meets everyone's needs. \ No newline at end of file + + Remember, this roadmap is a living document that evolves based on your input + and the direction the community decides to take. Your voice matters, and we're + excited to work together in building a robust notification infrastructure + solution that meets everyone's needs. + diff --git a/community/run-in-local-machine.mdx b/community/run-in-local-machine.mdx index bcd7ee0f..5a116d32 100644 --- a/community/run-in-local-machine.mdx +++ b/community/run-in-local-machine.mdx @@ -15,9 +15,19 @@ description: "Prerequisites and steps to run Novu in local machine. Learn how to Need help installing the requirements? Read more [here](/community/machine-setup). -We recommend having at least 8GB of RAM to run Novu on a local machine as Novu has multiple services running together with external services like redis, mongodb etc. - - + + We recommend having at least 8GB of RAM to run Novu on a local machine as Novu + has multiple services running together with external services like redis, + mongodb etc. + + + ### Setup the project @@ -27,16 +37,12 @@ your forked version of the project: 1. Clone the repository - - ```shell - git clone https://github.com/novuhq/novu.git - ```` - - - ```shell - git clone https://github.com/{YOUR_GITHUB_USER_NAME}/novu.git - ``` - + + ```shell git clone https://github.com/novuhq/novu.git ```` + + + ```shell git clone https://github.com/{YOUR_GITHUB_USER_NAME}/novu.git ``` + 2. Install all dependencies @@ -64,9 +70,21 @@ The `npm run start` will start the Jarvis CLI tool which allows you to run the - **start:node** - Runs the `@novu/node` package in watch mode - **start:notification-center** - Runs and builds the React package for the Novu notification center - - - + + + ### Set up your environment variables @@ -81,7 +99,7 @@ The command `npm run setup:project` creates default environment variables that - `S3_BUCKET_NAME`The name of the S3 Bucket - `S3_REGION`The AWS region of the S3 Bucket - `PORT`The port on which the API backend should listen on - - `FRONT_BASE_URL`The base url on which your frontend is accessible for the user. (e.g. web.novu.co) + - `FRONT_BASE_URL`The base url on which your frontend is accessible for the user. (e.g. dashboard.novu.co) - `DISABLE_USER_REGISTRATION` (default: false)If users should not be able to create new accounts. Possible values are: true, false - `REDIS_HOST`The domain / IP of your redis instance - `REDIS_PORT`The port of your redis instance @@ -113,75 +131,74 @@ The command `npm run setup:project` creates default environment variables that - `SENDGRID_API_KEY`The api key of the Sendgrid account used to send various emails - `MONGO_URL`The URL of your MongoDB instance - `MONGO_MAX_POOL_SIZE`The max pool size of the MongoDB connection - - `NOVU_API_KEY`The api key of web.novu.co used to send various emails + - `NOVU_API_KEY`The api key of dashboard.novu.co used to send various emails - `SENTRY_DSN`The DSN of sentry.io used to report errors happening in production - - - `NODE_ENV` (default: local)The environment of the app. Possible values are: dev, test, production, ci, local - - `PORT`The port on which the Worker app should listen on - - `STORE_ENCRYPTION_KEY`The encryption key used to encrypt/decrypt provider credentials - - `MAX_NOVU_INTEGRATION_MAIL_REQUESTS`The number of free emails that can be sent with the Novu email provider - - `NOVU_EMAIL_INTEGRATION_API_KEY`The Novu email provider Sentry API key - - `STORAGE_SERVICE`The storage service name: AWS, GCS, or AZURE - - `S3_LOCAL_STACK`The LocalStack service URL - - `S3_BUCKET_NAME`The name of the S3 Bucket - - `S3_REGION`The AWS region of the S3 Bucket - - `GCS_BUCKET_NAME`The name of the GCS Bucket - - `AZURE_ACCOUNT_NAME`The name of the Azure account - - `AZURE_ACCOUNT_KEY`The Azure account key - - `AZURE_HOST_NAME`The Azure host name - - `AZURE_CONTAINER_NAME`The Azure container name - - `AWS_ACCESS_KEY_ID`The AWS access key - - `AWS_SECRET_ACCESS_KEY`The AWS secret access key - - `REDIS_HOST`The domain / IP of your redis instance - - `REDIS_PORT`The port of your redis instance - - `REDIS_PASSWORD`Optional password of your redis instance - - `REDIS_DB_INDEX`The Redis database index - - `REDIS_CACHE_SERVICE_HOST`The domain / IP of your redis instance for caching - - `REDIS_CACHE_SERVICE_PORT`The port of your redis instance for caching - - `REDIS_DB_INDEX`The Redis cache database index - - `REDIS_CACHE_TTL`The Redis cache ttl - - `REDIS_CACHE_PASSWORD`The Redis cache password - - `REDIS_CACHE_CONNECTION_TIMEOUT`The Redis cache connection timeout - - `REDIS_CACHE_KEEP_ALIVE`The Redis cache TCP keep alive on the socket timeout - - `REDIS_CACHE_FAMILY`The Redis cache IP stack version - - `REDIS_CACHE_KEY_PREFIX`The Redis cache prefix prepend to all keys - - `REDIS_CACHE_SERVICE_TLS`The Redis cache TLS connection support - - `IN_MEMORY_CLUSTER_MODE_ENABLED`The flag that enables the cluster mode. It might be Redis or ElastiCache cluster, depending on the env variables set for either service. - - `ELASTICACHE_CLUSTER_SERVICE_HOST`ElastiCache cluster host - - `ELASTICACHE_CLUSTER_SERVICE_PORT`ElastiCache cluster port - - `REDIS_CLUSTER_SERVICE_HOST`Redis cluster host - - `REDIS_CLUSTER_SERVICE_PORTS`Redis cluster ports - - `REDIS_CLUSTER_DB_INDEX`Redis cluster database index - - `REDIS_CLUSTER_TTL`Redis cluster ttl - - `REDIS_CLUSTER_PASSWORD`Redis cluster password - - `REDIS_CLUSTER_CONNECTION_TIMEOUT`Redis cluster connection timeout - - `REDIS_CLUSTER_KEEP_ALIVE`Redis cluster TCP keep alive on the socket timeout - - `REDIS_CLUSTER_FAMILY`Redis cluster IP stack version - - `REDIS_CLUSTER_KEY_PREFIX`Redis cluster prefix prepend to all keys - - `MONGO_URL`The URL of your MongoDB instance - - `MONGO_MAX_POOL_SIZE`The max pool size of the MongoDB connection - - `NEW_RELIC_APP_NAME`The New Relic app name - - `NEW_RELIC_LICENSE_KEY`The New Relic license key - - `SEGMENT_TOKEN`The Segment Analytics token - - - - - `REACT_APP_ENVIRONMENT` The environment of the app. Possible values are: dev, test, production, ci, local - - `REACT_APP_API_URL` The base url on which your API backend would be accessible - - `REACT_APP_WS_URL` The base url on which your WebSocket service would be accessible - - `SKIP_PREFLIGHT_CHECK` (default: true)Solves a problem with React App dependency tree. - - When configuring different than default values for the API and WebSocket URLs, - in order for the Web app to apply the changes done to the `./env` file, it is - needed to run the script `pnpm envsetup`. This will generate a file - called `env-config.js` that will be copied inside of the `public` folder of - the application. Its purpose is to inject in the `window._env_`object the - chosen environment variables that manage the URLs the Web client will call to - access to the API backend and the WebSocket service. - - +{" "} + + - `NODE_ENV` (default: local)The environment of the app. Possible values are: + dev, test, production, ci, local - `PORT`The port on which the Worker app + should listen on - `STORE_ENCRYPTION_KEY`The encryption key used to + encrypt/decrypt provider credentials - `MAX_NOVU_INTEGRATION_MAIL_REQUESTS`The + number of free emails that can be sent with the Novu email provider - + `NOVU_EMAIL_INTEGRATION_API_KEY`The Novu email provider Sentry API key - + `STORAGE_SERVICE`The storage service name: AWS, GCS, or AZURE - + `S3_LOCAL_STACK`The LocalStack service URL - `S3_BUCKET_NAME`The name of the + S3 Bucket - `S3_REGION`The AWS region of the S3 Bucket - `GCS_BUCKET_NAME`The + name of the GCS Bucket - `AZURE_ACCOUNT_NAME`The name of the Azure account - + `AZURE_ACCOUNT_KEY`The Azure account key - `AZURE_HOST_NAME`The Azure host + name - `AZURE_CONTAINER_NAME`The Azure container name - `AWS_ACCESS_KEY_ID`The + AWS access key - `AWS_SECRET_ACCESS_KEY`The AWS secret access key - + `REDIS_HOST`The domain / IP of your redis instance - `REDIS_PORT`The port of + your redis instance - `REDIS_PASSWORD`Optional password of your redis instance + - `REDIS_DB_INDEX`The Redis database index - `REDIS_CACHE_SERVICE_HOST`The + domain / IP of your redis instance for caching - `REDIS_CACHE_SERVICE_PORT`The + port of your redis instance for caching - `REDIS_DB_INDEX`The Redis cache + database index - `REDIS_CACHE_TTL`The Redis cache ttl - + `REDIS_CACHE_PASSWORD`The Redis cache password - + `REDIS_CACHE_CONNECTION_TIMEOUT`The Redis cache connection timeout - + `REDIS_CACHE_KEEP_ALIVE`The Redis cache TCP keep alive on the socket timeout - + `REDIS_CACHE_FAMILY`The Redis cache IP stack version - + `REDIS_CACHE_KEY_PREFIX`The Redis cache prefix prepend to all keys - + `REDIS_CACHE_SERVICE_TLS`The Redis cache TLS connection support - + `IN_MEMORY_CLUSTER_MODE_ENABLED`The flag that enables the cluster mode. It + might be Redis or ElastiCache cluster, depending on the env variables set for + either service. - `ELASTICACHE_CLUSTER_SERVICE_HOST`ElastiCache cluster host - + `ELASTICACHE_CLUSTER_SERVICE_PORT`ElastiCache cluster port - + `REDIS_CLUSTER_SERVICE_HOST`Redis cluster host - + `REDIS_CLUSTER_SERVICE_PORTS`Redis cluster ports - + `REDIS_CLUSTER_DB_INDEX`Redis cluster database index - + `REDIS_CLUSTER_TTL`Redis cluster ttl - `REDIS_CLUSTER_PASSWORD`Redis cluster + password - `REDIS_CLUSTER_CONNECTION_TIMEOUT`Redis cluster connection timeout + - `REDIS_CLUSTER_KEEP_ALIVE`Redis cluster TCP keep alive on the socket timeout + - `REDIS_CLUSTER_FAMILY`Redis cluster IP stack version - + `REDIS_CLUSTER_KEY_PREFIX`Redis cluster prefix prepend to all keys - + `MONGO_URL`The URL of your MongoDB instance - `MONGO_MAX_POOL_SIZE`The max + pool size of the MongoDB connection - `NEW_RELIC_APP_NAME`The New Relic app + name - `NEW_RELIC_LICENSE_KEY`The New Relic license key - `SEGMENT_TOKEN`The + Segment Analytics token + + +{" "} + + - `REACT_APP_ENVIRONMENT` The environment of the app. Possible values are: + dev, test, production, ci, local - `REACT_APP_API_URL` The base url on which + your API backend would be accessible - `REACT_APP_WS_URL` The base url on + which your WebSocket service would be accessible - + `SKIP_PREFLIGHT_CHECK` (default: true)Solves a problem with React App + dependency tree. + + When configuring different than default values for the API and WebSocket + URLs, in order for the Web app to apply the changes done to + the `./env` file, it is needed to run the script `pnpm envsetup`. This will + generate a file called `env-config.js` that will be copied inside of + the `public` folder of the application. Its purpose is to inject in + the `window._env_`object the chosen environment variables that manage the + URLs the Web client will call to access to the API backend and the WebSocket + service. + + - `NODE_ENV` (default: local)The environment of the app. Possible values are: dev, test, production, ci, local diff --git a/community/windows-machine-setup.mdx b/community/windows-machine-setup.mdx index 4a1ce470..21c7bb0e 100644 --- a/community/windows-machine-setup.mdx +++ b/community/windows-machine-setup.mdx @@ -18,13 +18,14 @@ Download the .exe installation file for NVM for Windows [here](https://github.co After installing NVM for Windows, check that the installation is successfull and added to path by running `nvm -v` on a terminal. - After installing NVM, if you get `nvm: command not found` or see - no feedback from your terminal after you type `nvm -v`, simply **close - your current terminal, open a new terminal**, and try verifying again. + After installing NVM, if you get `nvm: command not found` or see no feedback + from your terminal after you type `nvm -v`, simply **close your current + terminal, open a new terminal**, and try verifying again. Once nvm is installed and working you can install any version of Node using the syntax `nvm install vX.Y.Z` and to switch to a node version, run `nvm use vX.Y.Z`. Novu requires node v20.8.1 or higher: + ```shell nvm install v18.17.0 nvm use v18.17.0 @@ -37,12 +38,15 @@ For package management, we use [PNPM](https://pnpm.io/) instead of npm or yarn t ```shell npm install -g pnpm ``` + ### MongoDB + for detailed instructions on installing MongoDB community edition on a Windows machine please refere to the MongoDB doc [here](https://www.mongodb.com/docs/manual/tutorial/install-mongodb-on-windows). After installation, whether you have installed as a Windows service or just the binaries, ensure that the MongoDB instance is started before you proceed. ### Redis + To install Redis for local development you'll first need to enable WSL2 (Windows Subsystem for Linux). detailed instructions for installing WSL can be found [here](https://learn.microsoft.com/en-us/windows/wsl/install) @@ -50,11 +54,13 @@ If you have WSL installed and enabled, you can easily install Redis using the na for Ubuntu or Debian which is the default distro installed by WSL, use the following commands 1. First install `lsb-release`, `curl` and `gpg` if you do not already have them: + ```shell sudo apt install lsb-release curl gpg ``` 2. Add the repository to the apt index, update it, and then install: + ```shell curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg @@ -63,10 +69,13 @@ echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://pack sudo apt-get update sudo apt-get install redis ``` + 3. Lastly, start the Redis server like so: + ```shell sudo service redis-server start ``` + ### Docker Docker is not a requirement to run Novu on your local machine, but if you would prefer to use Docker, Follow the installation guide on [docker website](https://docs.docker.com/desktop/install/windows-install/) to install **Docker Desktop** on your Windows machine. @@ -90,6 +99,7 @@ If needed, see: This will allow you to use [local.novu.co](http://local.novu.co) instead of [localhost](http://localhost) when accessing the service on your machine. Navigate to `C:\Windows\System32\drivers\etc` in the file explorer and open the `hosts` file with a text editor (like Notepad): + Note that this file can only be edited when running as an Administrator. diff --git a/concepts/controls.mdx b/concepts/controls.mdx new file mode 100644 index 00000000..69269320 --- /dev/null +++ b/concepts/controls.mdx @@ -0,0 +1,116 @@ +--- +title: "Controls" +--- + +Controls are defined using [JSON Schema](/recipes/json-schema) or Zod, providing a strong run-time validation system for your workflows. + +This ensures that you as the developer and your non-technical peers are speaking the same language. +Those responsible for styling and copy can edit with confidence, knowing their changes are tested in code. + +## Controls vs Payload + +**Control Schema** - For Non-Technical Peers and Developers. Managed in the Novu Dashboard UI, defined by developers and used by non-technical peers. + +**Payload Schema** - For Developers. Passed during the `novu.trigger` method, and controlled by the developer. + +## Common usecases + +- **Content** - Modify any static content: email subject, email body, push notification title, etc... +- **Styling** - Modify the styling of the content: button color, background color, font size, etc... +- **Behaviour** - Modify the behaviour of the content: show/hide a section, show/hide a button, etc... +- **Order** - Modify the order of the content: the order of the email sections, the order of the buttons, etc... +- **Actions** - Modify the behaviour of actions: digest duration, etc... +- **Other** - Any other use case that should be controller without modifying code + +## Step Controls + +Step Control schema defines the control passed during the `step` method. These controls can be modified and persisted in the Novu Dashboard UI. +The snippet below shows a configuration for the Step Control schema. If you don’t provide a schema, Typescript will infer the data type to `unknown`, reminding you of the best practice to specify your schema. + + + + ```tsx Zod Schema + import { z } from 'zod'; + + workflow("new-signup", async ({ step, payload }) => { + await step.email( + "send-email", + async (controls) => { + return { + subject: controls.subject, + body: render( + + ), + }; + }, + { + controlSchema: z.object({ + hideBanner: z.boolean().default(false), + subject: z.string().defaul('This is the default title'), + components: z.array(z.object({ + type: z.enum(['header', 'cta-row', 'footer']), + content: z.string() + })) + }) + } + ); + }); + ``` + + ```tsx JSON Schema + workflow("new-signup", async ({ step, payload }) => { + await step.email( + "send-email", + async (controls) => { + return { + subject: controls.subject, + body: render( + + ), + }; + }, + { + // Learn about JSON Schema here: https://json-schema.org/specification + controlSchema: { + // Always `object` + type: "object", + // Specify the properties to validate. Supports deep nesting. + properties: { + hideBanner: { type: "boolean", default: false }, + subject: { type: "string" }, + // Allowing no code control over the component in the Dashboard UI + components: { + type: "array", + items: { + type: "object", + }, + properties: { + subject: { type: "string" }, + content: { type: "string" }, + } + }, + }, + // Specify the array of which properties are required. + required: ["complianceFooter"], + // Used to enforce full type strictness, with no rogue properties. + additionalProperties: false, + // The `as const` is important to let Typescript know that this + // type won't change, enabling strong typing on `inputs` via type + // inference of the provided JSON Schema. + } as const, + } + ); + }); + ``` + + +## Supported Schema Types +- **ZodSchema** - A TypeScript-first schema declaration and validation library. +- **[JSON Schema](/recipes/json-schema)** - The most popular schema language for defining JSON data structures. + diff --git a/concepts/endpoint.mdx b/concepts/endpoint.mdx new file mode 100644 index 00000000..8d8e1280 --- /dev/null +++ b/concepts/endpoint.mdx @@ -0,0 +1,68 @@ +--- +title: "Bridge Endpoint" +--- + +Novu Framework requires a **single** `HTTP` endpoint (`/api/novu` or similar) to be exposed by your application. This endpoint is used to receive events from our Worker Engine. + +You can view the Bridge Endpoint as a webhook endpoint that Novu will call when it needs to retrieve contextual information for a given subscriber and notification. + +Using the `create-novu-app` command creates a Bridge application for you with a Bridge Endpoint ready to go. + +## The `serve` function + +We offer framework specific wrappers in form of an exported `serve` function that abstracts away: + +- Parsing the incoming request for `GET`, `POST`, `PUT` and `OPTIONS` requests +- HMAC header authentication +- Framework specific response and error handling + +Currently, we offer `serve` functions for the following frameworks: + +- [Next.js](/quickstart/nextjs) +- [Express.js](/quickstart/express) +- [Nuxt](/quickstart/nuxt) +- [h3](/quickstart/h3) +- [Remix](/quickstart/remix) +- [Sveltekit](/quickstart/svelte) + +## Writing a custom `serve` function + +If we currently don't support your framework, you can write a custom `serve` function like the following example: + +```ts +import { type Request, type Response } from "express"; +import { NovuRequestHandler, ServeHandlerOptions } from "@novu/framework"; + +export const serve = (options: ServeHandlerOptions): any => { + const requestHandler = new NovuRequestHandler({ + frameworkName: "express", + ...options, + handler: (incomingRequest: Request, response: Response) => ({ + method: () => incomingRequest.method, + headers: (key) => { + const header = incomingRequest.headers[key]; + return Array.isArray(header) ? header[0] : header; + }, + queryString: (key) => { + const qs = incomingRequest.query[key]; + return Array.isArray(qs) ? qs[0] : qs; + }, + body: () => incomingRequest.body, + url: () => + new URL( + incomingRequest.url, + `https://${incomingRequest.headers.get("host") || ""}` + ), + transformResponse: ({ body, headers, status }) => { + Object.entries(headers).forEach(([headerName, headerValue]) => { + response.setHeader(headerName, headerValue); + }); + + return response.status(status).send(body); + }, + }), + }); + + return requestHandler.createHandler(); +}; +``` diff --git a/platform/environments.mdx b/concepts/environments.mdx similarity index 61% rename from platform/environments.mdx rename to concepts/environments.mdx index 1fcc0cc1..a22c5949 100644 --- a/platform/environments.mdx +++ b/concepts/environments.mdx @@ -1,7 +1,6 @@ --- -title: 'Environments' -description: 'Learn about environments in Novu' -icon: 'house-building' +title: "Environments" +description: "Learn about environments in Novu" --- Novu runs all your requests in the context of an environment. By default, Novu creates two environments when your account was just created, `development` and `production`. @@ -34,25 +33,6 @@ Each environment will be accessed using a separate credential set: We suggest configuring these key sets based on your active environment, the same as you would use to manage different service credentials and serve them based on the current environment in which your code is deployed. -## Promoting pending changes to production +## Promoting changes to production -After making a change to a workflow or brand relating settings, this change will be added under the [changes](https://web.novu.co/changes?utm_campaign=docs-environments) page in the admin panel. - -A change is generated by making a difference between the `development` environment and the target `production` environment. All pending changes will be listed on the [changes](https://web.novu.co/changes?utm_campaign=docs-environments) page. - -A change can either be applied manually or by pressing the **Promote all changes** button. - - - - - -
- - Before pushing a change to production, make sure that the code associated with this change was pushed to production. This is specifically important when adding new variables to a workflow. - - -## API Reference Links - -- [Get Current Environment API](/api-reference/environments/get-current-environment/) -- [Get API Keys API](/api-reference/environments/get-api-keys/) -- [Regenerate API Keys API](/api-reference/environments/regenerate-api-keys/) \ No newline at end of file +When moving changes from the `development` environment to the `production` environment, we suggest using your CI/CD pipeline to promote the changes. diff --git a/getting-started/concepts.mdx b/concepts/introduction.mdx similarity index 68% rename from getting-started/concepts.mdx rename to concepts/introduction.mdx index 2218d255..891320df 100644 --- a/getting-started/concepts.mdx +++ b/concepts/introduction.mdx @@ -1,12 +1,11 @@ --- -title: 'Concepts' -description: 'Learn about the key concepts of Novu' -icon: 'building-columns' +title: "Introduction" +description: "Learn about the key concepts of Novu" --- ## Notification -A notification conveys information from source to recipient, triggered by a workflow acting as a message blueprint. Notifications can be individual or bundled as [digest](/workflows/digest) for user-friendliness. +A notification conveys information from source to recipient, triggered by a workflow acting as a message blueprint. Notifications can be individual or bundled as [digest](/workflows/digest) for user-friendliness. ## Messages @@ -18,7 +17,7 @@ All notifications are sent via a workflow. Each workflow acts as a container for ## Channel -A channel in Novu represents a configured provider, such as Sendgrid for email, to send notifications to your recipients. A channel is the communication-context domain with the recipients (subscribers). +A channel in Novu represents a configured provider, such as Sendgrid for email, to send notifications to your recipients. A channel is the communication-context domain with the recipients (subscribers). Most providers within Novu use credentials that you supply to deliver notifications on your behalf. These credentials and other settings are what make a configured channel. [Learn more](/channels-and-providers/introduction) @@ -47,13 +46,7 @@ Providers are the channel service providers (Twilio, Sendgrid, APN, etc,) you c - Production - Development -Novu uses the concept of environments to ensure logical separation of your data and configuration. This means that subscribers, and preferences created in one environment are **never** accessible to another. [Learn more about environments.](/platform/environments) - -## Handlebars & Helpers - -Handlebars is a simple templating language that generates HTML. - -Novu-specific Helpers make it possible to define custom iterators and other functionality that can invoke the passed block with a new context. [Learn more about helpers.](/content-creation-design/handlebars-helpers) +Novu uses the concept of environments to ensure logical separation of your data and configuration. This means that subscribers, and preferences created in one environment are **never** accessible to another. [Learn more about environments.](/platform/environments) ## Organization @@ -63,31 +56,11 @@ Switching your organization in the UI only affects the UI and does not deactivat Keep in mind that workflows and integration stores for one organization cannot be used for subscribers of other organizations. - - -To create a new organization, you interact with a drop down menu in the bottom of the left navigation bar within Novu’s application. Type the name of your new organization, and if the name is new, you will see a button that enables you to add a new one. - - - - -To switch between organizations, you again interact with the same drop down menu and switch between the organizations you have in your Novu account. - - +## Inbox -## Notification Center +The Inbox is a beautifully designed and highly functional in-app experience for your users that you can add to your product quickly and effortlessly. -Notification Center is a beautifully designed and highly functional in-app experience for your users that you can add to your product quickly and effortlessly. - -The core components of our Notification Center are: +The core components of the component: - A list of notifications with real-time updates - Notification preference management @@ -96,22 +69,10 @@ The core components of our Notification Center are: [Learn more](/notification-center/introduction) -## Preference +## Subscriber Preference A preference indicates a user's willingness to receive a particular type of notification. Preferences always belong to a user and can be paired with an identifier that represents the account or tenant that the user belongs to (for multi-tenant applications). [Learn more](/subscribers/preferences) -## Delay Actions - -The delay action awaits a specified amount of time before moving on to trigger the following steps of the workflow. [Learn more](/workflows/delay-action) - -## Digest - -The digest engine collects multiple trigger events, aggregates them into a single message and delivers it to the subscriber. [Learn more](/workflows/digest) - -## Layouts - -Novu allows the creation of layouts - a specific HTML design or structure to wrap content of email notifications. Layouts can be manipulated and assigned to new or existing workflows within the Novu platform, allowing users to create, manage, and assign these layouts to workflows, so they can be reused to structure the appearance of notifications sent through the platform. [Learn more](/content-creation-design/layouts) - ## Tenants -A tenant represents a group of users. As a developer, when your apps have organizations, they are referred to as tenants. Tenants in Novu provides the ability to tailor specific notification experiences to users of different **groups** or **organizations**. [Learn more about Novu Tenants.](/tenants/introduction) \ No newline at end of file +A tenant represents a group of users. As a developer, when your apps have organizations, they are referred to as tenants. Tenants in Novu provides the ability to tailor specific notification experiences to users of different **groups** or **organizations**. [Learn more about Novu Tenants.](/tenants/introduction) diff --git a/concepts/payload.mdx b/concepts/payload.mdx new file mode 100644 index 00000000..6039892f --- /dev/null +++ b/concepts/payload.mdx @@ -0,0 +1,57 @@ +--- +title: "Payload" +--- + +Workflow payload is the data passed during the `novu.trigger` method. This is useful for ensuring that the payload is correctly formatted and that the data is valid. + +## Payload Schema + +Payload schema is defining the payload passed during the `novu.trigger` method. This is useful for ensuring that the payload is correctly formatted and that the data is valid. + +```tsx +workflow( + "comment-on-post", + async ({ step, payload }) => { + await step.email("send-email", async () => { + return { + subject: `You have a new comment from: ${payload.author_name}.`, + body: render(), + }; + }); + }, + { + payloadSchema: { + // Always `object` + type: "object", + // Specify the properties to validate. Supports deep nesting. + properties: { + post_id: { type: "number" }, + author_name: { type: "string" }, + comment: { type: "string", maxLength: 200 }, + }, + // Specify the array of which properties are required. + required: ["post_id", "comment"], + // Used to enforce full type strictness, with no rogue properties. + additionalProperties: false, + // The `as const` is important to let Typescript know that this + // type won't change, enabling strong typing on `inputs` via type + // inference of the provided JSON Schema. + } as const, + } +); +``` + +## Passing Payload + +Here is an example of the validated payload during trigger: + +```tsx +novu.trigger("comment-on-post", { + to: "subscriber_id", + payload: { + post_id: 1234, + author_name: "John Doe", + comment: "Looks good!", + }, +}); +``` diff --git a/subscribers/preferences.mdx b/concepts/preferences.mdx similarity index 53% rename from subscribers/preferences.mdx rename to concepts/preferences.mdx index 63920399..a67ca2cb 100644 --- a/subscribers/preferences.mdx +++ b/concepts/preferences.mdx @@ -1,6 +1,5 @@ --- title: "Preferences" -icon: "layer-group" --- Novu provides a way to store user preferences in the subscribers data model. @@ -14,20 +13,13 @@ This allows subscribers to specify and manage their preferences, without your in ## Workflow level channel preferences -When creating a new workflow on the Web platform, you can specify default preferences for the subscribers in channel settings. They will be used unless the subscriber overrides them by his own custom preference. +When creating a new workflow, you can specify default preferences for the subscribers in channel settings. They will be used unless the subscriber overrides them by his own custom preference. -This will allow you to create sensible defaults but still provide the user with the ability to override them. Template level preference can be managed in channel settings. All channels are `ON` unless specified otherwise. - -`Workflow Settings > Channels` - - +This will allow you to create sensible defaults but still provide the user with the ability to override them. All channels are `ON` unless specified otherwise. ## Subscriber level channel preferences -Our notification center component will show a user the available preferences, user will be able to modify on the channel level. Critical workflows will be excluded from the list. Click on cog (setting) icon on notification center component to open subscriber channel preferences page. +Our Inbox component will show a user the available preferences, user will be able to modify on the channel level. Critical workflows will be excluded from the list. Click on cog (setting) icon on notification center component to open subscriber channel preferences page. Only channels with a matched step will be returned from the API in - notification center preference page. In case no channel content was found, the + Ibox preference page. In case no channel content was found, the API will return an empty array. ## Global level channel preferences -Since v0.20.0 Subscribers can set global channel preferences, which override individual settings. For instance, if there are 10 workflows, and a subscriber wants to disable SMS notifications for all of them, they can do so with a single global preference. -### Get subscriber global preference - - - - -```javascript -import { Novu } from '@novu/node'; - -const novu = new Novu(''); - -await novu.subscribers.getGlobalPreference("subscriberId" ); -``` - - - - -[Get subscriber's global preference API](/api-reference/subscribers/get-subscriber-global-preferences) - -### Update subscriber global preference - - - - - -```javascript -import { Novu } from '@novu/node'; - -const novu = new Novu(''); - -// enable in-app channel and disable email channel -await novu.subscribers.updateGlobalPreference("subscriberId", { - enabled: true, - preferences: [{ - type: "in_app" - enabled: true - }, { - type: "email" - enabled: false - }] -}); -``` - - - - -[Update subscriber's global preference API](/api-reference/subscribers/update-subscriber-global-preference) - -Global subscriber preference can be updated and accessed using API and SDK only. Currently we have not added support of global subscriber preference in UI. + + Global subscriber preference can be updated and accessed using API and SDK + only. Currently we have not added support of global subscriber preference in + UI. + ## Exclude workflows from preferences (critical workflow) In some cases, you don't want the subscriber to be able to unsubscribe from mandatory notifications such as Account Verification, Password Reset, etc... -In those cases you can turn off the toggle `Users will be able to manage subscriptions` in channel settings. Workflow will become `critical`, once this toggle is turned `OFF`. By default, every workflow is `non-critical` and subscribers can manage channel preferences irrespective of workflow-level channel preferences. Critical workflow will not show on the subscriber preferences page. - -## Get subscriber preferences - - - - - -```javascript -import { Novu } from "@novu/node"; - -const novu = new Novu(""); - -// 111 is subscriberId -await novu.subscribers.getPreference("111"); -``` - - - - -```php -use Novu\SDK\Novu; - -$novu = new Novu(''); - -// 111 is subscriberId -$novu->getSubscriberPreferences('111')->toArray(); -``` - - - - -## Get subscriber preference by level - - - - - -```javascript -import { Novu, PreferenceLevelEnum } from '@novu/node'; - -const novu = new Novu(''); -// Get global level preference -await novu.subscribers.getPreferenceByLevel("subscriberId", PreferenceLevelEnum.GLOBAL) - -// Get template level preference -await novu.subscribers.getPreferenceByLevel("subscriberId", PreferenceLevelEnum.TEMPLATE); -``` - - - - -## Update subscriber preference for a workflow - - - - - -```javascript -import { Novu, ChannelTypeEnum } from "@novu/node"; - -const novu = new Novu(""); - -// enable in_app channel -await novu.subscribers.updatePreference("subscriberId", "workflowIdentifier", { - enabled: true, - channel: { type: ChannelTypeEnum.IN_APP, enabled: true }, -}); - -// enable email channel -await novu.subscribers.updatePreference("subscriberId", "workflowIdentifier", { - enabled: true, - channel: { type: ChannelTypeEnum.EMAIL, enabled: true }, -}); -``` - - - - -```php -use Novu\SDK\Novu; - -$novu = new Novu(''); - -// enable in_app channel -$novu->updateSubscriberPreference('subscriberId', 'workflowIdentfier', [ - 'enabled' => true - 'channel' => [ - 'type' => 'in_app', - 'enabled' => true - ] -]); - -// enable email channel -$novu->updateSubscriberPreference('subscriberId', 'workflowIdentfier', [ - 'enabled' => true - 'channel' => [ - 'type' => 'email', - 'enabled' => true - ] -]); -``` - - - +In those cases you can mark a workflow as `critical` in workflow settings. By default, every workflow is `non-critical` and subscribers can manage channel preferences irrespective of workflow-level channel preferences. Critical workflow will not show on the subscriber preferences page. ## Order of priority of preferences @@ -315,13 +159,15 @@ $novu->updateSubscriberPreference('subscriberId', 'workflowIdentfier', [ This field can only be changed using API.
- - In the case of a new workflow, the subscriber will inherit all preferences - from the workflow.However, after subsequent preference updates, the - subscriber's preferences will not inherit workflow-level preferences. - +{" "} + + In the case of a new workflow, the subscriber will inherit all preferences + from the workflow.However, after subsequent preference updates, the + subscriber's preferences will not inherit workflow-level preferences. + You can turn off all workflow's channels for a subscriber using [subscriber preference](/subscribers/preferences#subscriber-level-channel-preferences). If all channels are off, then the subscriber will not receive any message from any channel step and hence workflow is disabled for that subscriber + diff --git a/concepts/subscribers.mdx b/concepts/subscribers.mdx new file mode 100644 index 00000000..2d3c6edc --- /dev/null +++ b/concepts/subscribers.mdx @@ -0,0 +1,193 @@ +--- +title: "Subscribers" +--- + +In Novu, we call the entities designed to receive notifications as `Subscribers`. Each subscriber is unique and identified by a unique subscriberId. + + + We recommend using the internal unique id your application uses for a specific + user as the `subscriberId`. + + +Each subscriber has the following data points: + +- **User Data** - Data stored in the subscriber object that you can easily access in your notification templates. This contains basic info such as first name, last name, avatar, locale, email, and phone. This data is fixed and structured. +- **Custom Data** - Apart from the above fixed structured user data, any unstructured custom data such as user's address, nationality, height, etc can also be stored in the `data` field using key-value pairs. +- **Channel Specific Credentials** - `deviceTokens` required to send push notifications and `webhookUrl` for chat channel providers can also be stored. + +## Subscriber attributes + +| Field | Type | Required | Example | +| :----------- | :----- | :------- | :------------------------------------ | +| subscriberId | string | true | b0bea066-f5fe-11ed-b67e-0242ac120002 | +| firstName | string | false | John | +| lastName | string | false | Doe | +| email | string | false | john.doe@domain.org. | +| phone | string | false | +13603963366 | +| locale | string | false | en | +| avatar | string | false | https://example.com/images/avatar.jpg | +| data | object | false | `{"key": "value"}` | + +## Subscriber response schema + +**Novu subscriber object** + +```json +{ + "_id": "NOVU_GENERATED_SUBSCRIBER_ID", + "_organizationId": "NOVU_GENERATED_ORG_ID", + "_environmentId": "NOVU_GENERATED_ENV_ID", + "firstName": "John", + "lastName": "Doe", + "subscriberId": "subscriberId", + "email": "[john.doe@org.com](mailto:john.doe@org.com)", + "phone": "+98712345670" + "data": { + "custome_key_1" : "custom_value_1", + "custome_key_2" : "custom_value_2" + } + "channels": [ + { + "credentials": { + "deviceTokens": [ + "token1", + "token2" + ] + }, + "_integrationId": "NOVU_GENERATED_INTEGRATION_ID", + "providerId": "fcm" + }, + { + "credentials": { + "webhookUrl": "URL" + }, + "_integrationId": "NOVU_GENERATED_INTEGRATION_ID", + "providerId": "discord" + } + ], + "deleted": false, + "createdAt": "2022-10-13T17:40:53.231Z", + "updatedAt": "2022-10-13T17:41:53.238Z", + "__v": 0, + "isOnline": false, + "lastOnlineAt": "2022-10-13T17:41:53.238Z", + "avatar": "AVATAR_URL", + "id": "NOVU_GENERATED_SUBSCRIBER_ID" +} +``` + +## Creating a subscriber + +We support creating new subscriber using two ways, `Ahead of Trigger` means adding subscribers before triggering notification or `Just-in-time` means sending complete subscriber data in `to` field while triggering. + +### Just-in-time + +A non-existing subscriber can be added by sending subscriber data in `to` field of the trigger method. If any subscriber with provided `subscriberId` does not exists, a new subscriber will be created. In this case, subscriber will be created first and then the trigger will be executed synchronously. + + + + ```jsx + import { Novu } from '@novu/node'; + + const novu = new Novu(''); + + await novu.trigger('', { + to: { + subscriberId: '111', + email: 'john.doe@domain.com', + firstName: 'John', + lastName: 'Doe', + phone: '+13603963366', + }, + payload: { + customVariable: 'variableValue', + organization: { + logo: 'https://organization.com/logo.png', + }, + }, + }); + ``` + + + ```php + use Novu\SDK\Novu; + + $novu = new Novu(''); + + $novu->triggerEvent([ + 'name' => '', + 'to' => [ + 'subscriberId' => '111', + 'email' => 'john.doe@domain.com', + 'firstName' => 'John', + 'lastName' => 'Doe', + 'phone' => '+13603963366' + ] + 'payload' => [ + 'customVariable' => 'variableValue', + 'organization' => [ + 'logo' => 'https://organization.com/logo.png', + ] + ], + ]); + ``` + + + + +You can also pass only the `subscriberId` during a request, and hydrate the data directly from your database or other sources during the trigger execution. This is useful when you don't want to store all the subscriber data in Novu. + +### Migration (Optional) + +Create the subscriber and then trigger the notification to this subscriber. Here `subscriberId` is the required field and other fields are optional. + + + +```jsx +import { Novu } from '@novu/node'; + +const novu = new Novu(''); + +await novu.subscribers.identify('111', { + email: 'john.doe@domain.com', + firstName: 'John', + lastName: 'Doe', + phone: '+13603963366', + avatar: 'https://example.com/images/avatar.jpg', + locale: 'en-US', + data: { customKey1: 'customVal1', customKey2: 'customVal2' }, +}); + +```` + + + +```php +use Novu\SDK\Novu; + +$novu = new Novu(''); + +$novu->createSubscriber([ + 'subscriberId' => '111', + 'email' => 'john.doe@domain.com', + 'firstName' => 'John', + 'lastName' => 'Doe', + 'phone' => '+13603963366', + 'avatar' => 'https://example.com/images/avatar.jpg', + 'locale' => 'en', + 'data' => [ + 'customKey1' => 'customVal1', + 'customKey2' => 'customVal2' + ] +]); +```` + + + + +Novu will create a subscriber if one does not exist and will update the existing subscriber based on the `identify` payload. You can call this function during registration or signup to make sure the subscriber data is up-to-date, if you wish to save additional attributes with a subscriber, you can pass additional custom data in the **data** field as key-value pairs. + +## Bulk Subscriber Creation + +You can create subscribers in bulk _(up to 500 at once)_ via the SDKs or [API](/api-reference/subscribers/bulk-create-subscribers). + diff --git a/concepts/tenants.mdx b/concepts/tenants.mdx new file mode 100644 index 00000000..633eb33e --- /dev/null +++ b/concepts/tenants.mdx @@ -0,0 +1,81 @@ +--- +title: "Tenants" +description: "Learn all about Tenants" +--- + +Multi-tenancy is a common use case for a lot of companies that have multiple organizations that use their applications. In some cases, there is a need to separate the behavior of the notification system depending on the individual tenants. + +Currently, Novu supports the following customizations: + +- Workflow level customizations +- Integration customizations + +## Tenant Management + +Tenants can be created and modified via the [API](http://docs.novu.co/api-reference/tenants/get-tenants) or [Web](https://dashboard.novu.co/tenants?utm_campaign=docs-tenants). Each tenant can have multiple fields on it: + +- Identifier - The identifier is a unique value, and can be used later when pointing to this tenant during trigger calls. +- Name - A human-readable name of the tenant. +- Data - A custom data object that can store information about the tenant. This data can be later accessed inside workflows. + +## Workflow Level Customizations + +When triggering a workflow, it is possible to pass the tenant information (id or object, in case of an object, novu will upsert the tenant if it’s not existing) like so: + + + + ```javascript + import { Novu } from '@novu/node'; + + const novu = new Novu(""); + + await novu.trigger('', + { + to: { + subscriberId: '', + }, + payload: { + name: "Hello World", + }, + actor: "actorId" + tenant: "tenantIdentifier" + } + ); + +```` + + +```php + use Novu\SDK\Novu; + + $novu = new Novu(''); + + $novu->triggerEvent([ + 'name' => '', + 'payload' => [ + 'name' => 'Hello' + ], + 'to' => [ + 'subscriberId' => '' + ], + 'actor': "actorId" + 'tenant': "tenantIdentifier" + ]); +```` + + + + +## Integration Level Customizations + +When creating an integration, you can create a condition where you can specify a delivery provider for a channel when a particular tenant id is matched. + +The delivery provider specified will only be used if the trigger is executed with the tenant id. + + + + + + + + diff --git a/subscribers/topics.mdx b/concepts/topics.mdx similarity index 73% rename from subscribers/topics.mdx rename to concepts/topics.mdx index 44b37dcb..cc533a8b 100644 --- a/subscribers/topics.mdx +++ b/concepts/topics.mdx @@ -1,12 +1,12 @@ --- -title: 'Topics' -icon: 'people' +title: "Topics" --- Novu offers a simple API providing an easy interface for triggering notifications to multiple subscribers at once. It is called Topics and allows the users to manage their bulk notifications without having to do complex loop implementations. A topic is identified by a custom key that is provided by the user. This custom key will be the identifier for the topic. -The topic key should be unique and can't be changed once chosen. Novu also safe guards for key uniqueness behind the scenes. + The topic key should be unique and can't be changed once chosen. Novu also + safe guards for key uniqueness behind the scenes. Users can also assign a name to a topic. This name doesn't need to be unique and it can be changed using the API. So far, it is for descriptive goals. @@ -18,13 +18,13 @@ A topic would get assigned different subscribers that will receive a notificatio In order to be able to send a notification to a topic, first the user needs to create one. It can be done like this: ```typescript -import { Novu } from '@novu/node'; +import { Novu } from "@novu/node"; const novu = new Novu(""); const result = await novu.topics.create({ - key: 'unique-topic-identifier', - name: 'descriptive-topic-name', + key: "unique-topic-identifier", + name: "descriptive-topic-name", }); ``` @@ -33,11 +33,11 @@ If successful this will return the internal Id generated by Novu and the topic k That topic key can be used to retrieve the whole Topic entity: ```typescript -import { Novu } from '@novu/node'; +import { Novu } from "@novu/node"; const novu = new Novu(""); -const key = 'unique-topic-identifier'; +const key = "unique-topic-identifier"; const result = await novu.topics.get(key); ``` @@ -47,12 +47,12 @@ const result = await novu.topics.get(key); Descriptive name given during creation of topic can be changed later. This can be done like this: ```typescript -import { Novu } from '@novu/node'; +import { Novu } from "@novu/node"; const novu = new Novu(""); -const topicKey = 'posts:comment:12345'; -const topicName = 'Post Comments'; +const topicKey = "posts:comment:12345"; +const topicName = "Post Comments"; const result = await novu.topics.rename(topicKey, topicName); ``` @@ -66,7 +66,11 @@ This will return the whole Topic information, including the subscribers if havin Adding subscribers to the topic is the main usecase of topic. A topic is like a group of subscribers. A notification can be sent to all subscribers of a topic at once - Before adding subscribers to a topic, it's crucial to ensure that they are correctly identified in the Novu system. This identification is done using the [identify method](/subscribers/subscribers#create-a-subscriber) or API. When a subscriber is added to a topic, Novu uses the identification information to pick up the email or phone number associated with the subscriber. + Before adding subscribers to a topic, it's crucial to ensure that they are + correctly identified in the Novu system. This identification is done using the + [identify method](/subscribers/subscribers#create-a-subscriber) or API. When a + subscriber is added to a topic, Novu uses the identification information to + pick up the email or phone number associated with the subscriber. ```typescript @@ -94,12 +98,11 @@ This call will return a response in the shape of: The field `succeeded` will return the array of subscriber ids that have been correctly assigned to the topic. The field `failed` will show up only if there has been any subscriber that couldn't been assigned to the topic. This probably will happen if there is any typo in the given subscriberId or if the given subscriberId doesn't belong to the environment and the organization where the topic has been created. - ### Create topic on the fly -To facilitate flows we allow to create a topic on the fly when adding subscribers. +To facilitate flows we allow to create a topic on the fly when adding subscribers. -If the topic key used does not exist for the selected environment and organization, Novu will create a new topic with name `Autogenerated-` and the subscribers will be assigned to this newly created topic. +If the topic key used does not exist for the selected environment and organization, Novu will create a new topic with name `Autogenerated-` and the subscribers will be assigned to this newly created topic. After that, that topic created on the fly can be managed in the same ways as the ones created in the normal way. @@ -108,19 +111,23 @@ After that, that topic created on the fly can be managed in the same ways as the There would be times when it would be needed to verify if a certain subscriber belongs to a topic in order to decide what to do with that subscriber. The API call to achieve that is the following: ```typescript -import { Novu } from '@novu/node'; +import { Novu } from "@novu/node"; const novu = new Novu(""); -const externalSubscriberId = 'external-subscriber-id-1'; -const topicKey = 'posts:comment:12345'; +const externalSubscriberId = "external-subscriber-id-1"; +const topicKey = "posts:comment:12345"; -const response = await novu.topics.getSubscriber(topicKey, externalSubscriberId); +const response = await novu.topics.getSubscriber( + topicKey, + externalSubscriberId +); ``` -The subscriberId to use in this API call is the one used as subscriber identifier. -The choice was made to make easier for the user to use this without making extra calls to Novu. + The subscriberId to use in this API call is the one used as subscriber + identifier. The choice was made to make easier for the user to use this + without making extra calls to Novu. ## Remove a subscriber from a Topic @@ -146,17 +153,19 @@ Where `subscribers` will be an array of the subscriberIds we want to remove fr An existing topic can be deleted using topic-key as topic-key is the unique identifier for a topic. ```typescript -import { Novu } from '@novu/node'; +import { Novu } from "@novu/node"; const novu = new Novu(""); -const key = 'unique-topic-identifier'; +const key = "unique-topic-identifier"; const result = await novu.topics.delete(key); ``` -A topic can only be deleted if it doesn't have any subscriber added. When trying to delete a topic with subscribers a conflict error will be returned. This is done to prevent accidental topic deletions. + A topic can only be deleted if it doesn't have any subscriber added. When + trying to delete a topic with subscribers a conflict error will be returned. + This is done to prevent accidental topic deletions. ## Trigger workflow to a topic @@ -164,10 +173,10 @@ A topic can only be deleted if it doesn't have any subscriber added. When trying A workflow can be triggered to a single subscriber and an array of subscribers. While doing so, one need to send subscriberIds in `to` field. In case of trigger, we have already stored subscriberIds in a topic, so a topic key can be used to trigger a workflow. Workflow will be triggered to all subscribers in the topic. ```typescript -const topicKey = 'posts:comment:12345'; +const topicKey = "posts:comment:12345"; -await novu.trigger('', { - to: [{ type: 'Topic', topicKey: topicKey }], +await novu.trigger("", { + to: [{ type: "Topic", topicKey: topicKey }], payload: {}, }); ``` @@ -177,12 +186,12 @@ await novu.trigger('', { Workflow can be triggered to multiple subscribers by using an array of topics. ```typescript -const topicKey = ''; +const topicKey = ""; -await novu.trigger('', { +await novu.trigger("", { to: [ - { type: 'Topic', topicKey: topicKey }, - { type: 'Topic', topicKey: 'Another Topic Key' }, + { type: "Topic", topicKey: topicKey }, + { type: "Topic", topicKey: "Another Topic Key" }, ], payload: {}, }); @@ -193,12 +202,12 @@ await novu.trigger('', { To exclude the actor responsible for the action of a triggered topic event, you must add the subscriberId of that actor when triggering that event. ```typescript -const topicKey = 'posts:comment:12345'; +const topicKey = "posts:comment:12345"; -await novu.trigger('', { - to: [{ type: 'Topic', topicKey: topicKey }], +await novu.trigger("", { + to: [{ type: "Topic", topicKey: topicKey }], payload: {}, - actor: { subscriberId: '' }, + actor: { subscriberId: "" }, }); ``` @@ -227,7 +236,7 @@ User X makes a comment, other users (A, B) who've already commented before shoul color="#16a34a" href="/api-reference/topics/subscribers-addition" > - - - - - No, adding subscribers into topic does not change subscriber's individual [subscriber preference](/subscribers/preferences). Topic is mainly used to group subscribers and fan out workflow triggered notifications to all subscribers at once. + No, adding subscribers into topic does not change subscriber's individual + [subscriber preference](/subscribers/preferences). Topic is mainly used to + group subscribers and fan out workflow triggered notifications to all + subscribers at once. diff --git a/concepts/trigger.mdx b/concepts/trigger.mdx new file mode 100644 index 00000000..46520967 --- /dev/null +++ b/concepts/trigger.mdx @@ -0,0 +1,52 @@ +--- +title: "Trigger" +description: "Managing Trigger Events from Request to Processing" +--- + +## Trigger Request + +A trigger request is the initial step in handling a trigger event. It contains crucial details such as the template identifier, a list of subscribers who will receive the notification, the payload of the notification, and any overrides that need to be applied. + +## Trigger Endpoint + +Upon sending the request to the `/event/trigger` endpoint, a series of essential steps are initiated: + +1. **Subscriber Mapping and Validation:** The first step involves mapping and validating the subscribers for the specified event. This ensures that notifications are sent to the correct recipients. + +2. **Workflow Validation:** Following subscriber validation, the workflow associated with the event is validated. This validation process considers factors such as the active status to determine if it meets the necessary criteria for processing. + +3. **Attachment Upload:** Once the validation process is successfully completed, any attachments associated with the event are uploaded to the designated storage service. + +4. **Event Queuing:** The trigger event, now enriched with mapped subscribers and attachment links, is appended to the **trigger event queue**. This queuing mechanism optimizes response times, ensuring efficient event processing. + +## Trigger Event Processing + +When an event is picked up by the **trigger queue worker**, the processing phase begins. Here's what happens: + +- **Subscriber Upsert:** The worker validates the subscribers associated with the event and either creates or updates the subscriber with the information passed in the `to` object. +- **Notification Entity Creation:** For each subscriber listed in the trigger event, a corresponding notification entity is created. This entity contains essential data related to the organization, template, subscriber, and event payload. +- **Job Creation:** Based on the notification template's defined steps, jobs are generated. These jobs are responsible for carrying out specific tasks related to the event notification. Additionally, the notification entity is updated with a "channels" field generated from these steps, indicating the communication channels through which notifications will be sent. + +## Jobs + +Jobs play a crucial role in the trigger event lifecycle. They are created based on the steps outlined in the workflow. + +### Job Statuses + +| Status | Description | +| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **PENDING** | This status is assigned to a job before it is added to the worker queue. It indicates that the job is waiting to be processed. | +| **QUEUED** | After the initial validation and just before adding a job to the worker queue, it is set to `QUEUED`. This status signifies that the job is ready for processing but is awaiting its turn in the queue. | +| **RUNNING** | When a job is picked up by a worker from the queue, its status is changed to `RUNNING`. This indicates that the job is currently being processed by a worker. | +| **COMPLETED** | Once a job has been successfully executed and processed, its status is changed to `COMPLETED`. This signifies that the job has been successfully completed. | +| **FAILED** | If a job encounters an issue during processing or execution, its status is changed to `FAILED`. This indicates that the job has not been successfully completed, and there may have been errors or problems during processing. | +| **DELAYED** | The `DELAYED` status is applied to specific types of jobs, such as `digest` or `delay` jobs, to indicate that they are delayed and not immediately processed. For `digest` jobs, it means that the digesting process is running or scheduled for a later time. For `delay` jobs, it signifies that the job is set to be executed at a specified delay time. | +| **CANCELED** | When a job is canceled for any reason, its status is set to `CANCELED`. This indicates that the job will not be processed further and is effectively removed from the processing queue. | +| **MERGED** | The `MERGED` status is assigned to events that are part of a `digest`. It indicates that an event will be merged into the digesting event. In a digesting process, there is typically a primary or initial event that serves as the digesting event, and subsequent events are merged into it. Instead of having a separate `COMPLETED` status for these merged events, they are marked as `MERGED` to indicate their specific role in the digesting process. | +| **SKIPPED** | The `SKIPPED` status is used in the context of backoff versions of digesting. In this scenario, the first event's digesting is skipped, and the second event takes on the digesting role. The `SKIPPED` status is applied to the first event's digesting, indicating that it was intentionally skipped in the digesting process. Subsequent events may be merged into the second event's digesting process, as explained with the `MERGED` status. The `SKIPPED` status helps differentiate the skipped event from others in the digesting sequence. | + +**Example:** + + + {" "} + diff --git a/concepts/workflows.mdx b/concepts/workflows.mdx new file mode 100644 index 00000000..a92cc733 --- /dev/null +++ b/concepts/workflows.mdx @@ -0,0 +1,51 @@ +--- +title: "Workflows" +description: "Workflows are at the core of responding to events in your system with notifications. Novu Framework enables you to declare type-safe, validated, and version-controlled Workflows with code." +--- + +A workflow holds the entire flow of steps (nodes) that are sent to the subscriber. This is where all the different channels are tied together under a single entity. + +## Structure + +A workflow acts as the blueprint for the notifications that will be sent. This is where all the different channels, filters, rules and actions are tied together under a single entity. + + + +## Workflow Components + + + + Every workflow will have a name and an identifier that are used to uniquely identify each workflow. + + + The "Trigger" refers to an event or action that initiates the workflow. It signifies a call to the Novu API with a specified workflow ID, along with the necessary payload data that the workflow content will utilize. + + + Within the Novu framework, steps are categorized into various types, each of which is linked with at least one corresponding action: + + #### Channel Steps + - **Email** (examples include Sendgrid, Postmark) + - **Inbox** (such as feeds, toasts, banners) + - **Push** (such as APNS, FCM) + - **SMS** (examples include Twilio, Telnyx) + - **Chat** (such as Slack, Microsoft Teams, and Discord) + + #### Action Steps + - **Delay** (to pause the workflow for a specified duration) + - **Digest** (to group multiple notifications into a single message) + - **Custom** (to execute custom logic like calling an HTTP API, database query, etc.) + + + + +## Execution of Workflow Steps + +Once a workflow is initiated by its trigger, the steps within the workflow are executed in a specific sequence. This sequential execution ensures that each step is completed before the next one begins, maintaining a controlled and orderly flow of the notification process. Here's what you need to know about this process: + +Each step in the workflow is activated one after the other, in the order they are listed within the workflow. This method ensures that each step is given the necessary attention and that dependencies or prerequisites of later steps are adequately met. +Sequential execution provides a predictable and reliable workflow process, ensuring that messages are sent out in an orderly manner and that each step's output can appropriately influence subsequent steps. +
+Read more about building workflows [here](/workflow/introduction). \ No newline at end of file diff --git a/content-creation-design/brand.mdx b/content-creation-design/brand.mdx index 730be182..f4b826d8 100644 --- a/content-creation-design/brand.mdx +++ b/content-creation-design/brand.mdx @@ -1,14 +1,11 @@ --- -title: 'Brand' -description: '' -icon: 'sparkle' +title: "Brand" +description: "" +icon: "sparkle" --- Through Novu's web dashboard, you have the power to effortlessly tailor your brand settings, streamlining the process of creating notifications. Customize key elements such as your logo, which will be seamlessly integrated into your email communications. For those utilizing Novu's In-App notification center, the ability to configure the primary font and its corresponding color awaits you. This empowers you to craft an experience for your users that resonates with your unique brand identity. - + diff --git a/content-creation-design/handlebars-helpers.mdx b/content-creation-design/handlebars-helpers.mdx index c183973a..6f9b7ff6 100644 --- a/content-creation-design/handlebars-helpers.mdx +++ b/content-creation-design/handlebars-helpers.mdx @@ -1,39 +1,39 @@ --- -title: 'Handlebars & Helpers' -description: '' -icon: 'brackets-curly' +title: "Handlebars & Helpers" +description: "" +icon: "brackets-curly" --- + # Handlebars -The Novu notification content editor relies on Handlebars.js to establish the logic and control flow of our workflows. -For a deeper comprehension of Handlebars.js, you can refer to its [documentation](https://handlebarsjs.com/guide/#what-is-handlebars). +The Novu notification content editor relies on Handlebars.js to establish the logic and control flow of our workflows. +For a deeper comprehension of Handlebars.js, you can refer to its [documentation](https://handlebarsjs.com/guide/#what-is-handlebars). This resource offers comprehensive insights into the utilization of Handlebars.js in crafting dynamic and adaptable notification content within the Novu platform. -| Helper | Description | Example | Output | -| --------------- | -------------------------------------------------------- | ------------------------------------------------ | ---------------------------------------------------- | -| if | Conditionally renders a block. Falsy values won't be rendered. | `{{#if author}}

{{firstName}} {{lastName}}

{{/if}}` | Renders `

Yehuda Katz

` if `author` is truthy. | -| includeZero | `includeZero=true` treats the conditional as not empty, handling 0 differently. | `{{#if 0 includeZero=true}}

Does render

{{/if}}` | Renders `

Does render

` for 0. | -| Sub-Expressions | Create custom logic helpers and use them in sub-expressions. | `{{#if (isdefined value1)}}true{{else}}false{{/if}}` | Renders `true` or `false` based on custom helper. | -| unless | Inverse of if helper. Renders a block if the expression is falsy. | `{{#unless license}}

WARNING

{{/unless}}` | Renders warning if `license` is falsy. | -| each | Iterate over a list. Use this to reference the iterated element. | `{{#each people}}
  • {{this}}
  • {{/each}}` | Renders list items. | -| with | Change context for template parts. | `{{#with person}}{{firstname}} {{lastname}}{{/with}}` | Renders `firstname` and `lastname` from the context. | -| Nested each | Access iteration variables in nested each blocks. | `{{#each array}}{{@index}}: {{this}}{{/each}}` | Renders index and item in each iteration. | -| Nested with | Use with block parameters for clear code. | See above example with nested with and complex template. | Renders nested context values. | -| | You can use else section to handle empty values. | `{{#with city}}...{{else}}No city found{{/with}}` | Handles empty context case. | - - +| Helper | Description | Example | Output | +| --------------- | ------------------------------------------------------------------------------- | ---------------------------------------------------------------- | ----------------------------------------------------- | +| if | Conditionally renders a block. Falsy values won't be rendered. | `{{#if author}}

    {{firstName}} {{lastName}}

    {{/if}}` | Renders `

    Yehuda Katz

    ` if `author` is truthy. | +| includeZero | `includeZero=true` treats the conditional as not empty, handling 0 differently. | `{{#if 0 includeZero=true}}

    Does render

    {{/if}}` | Renders `

    Does render

    ` for 0. | +| Sub-Expressions | Create custom logic helpers and use them in sub-expressions. | `{{#if (isdefined value1)}}true{{else}}false{{/if}}` | Renders `true` or `false` based on custom helper. | +| unless | Inverse of if helper. Renders a block if the expression is falsy. | `{{#unless license}}

    WARNING

    {{/unless}}` | Renders warning if `license` is falsy. | +| each | Iterate over a list. Use this to reference the iterated element. | `{{#each people}}
  • {{this}}
  • {{/each}}` | Renders list items. | +| with | Change context for template parts. | `{{#with person}}{{firstname}} {{lastname}}{{/with}}` | Renders `firstname` and `lastname` from the context. | +| Nested each | Access iteration variables in nested each blocks. | `{{#each array}}{{@index}}: {{this}}{{/each}}` | Renders index and item in each iteration. | +| Nested with | Use with block parameters for clear code. | See above example with nested with and complex template. | Renders nested context values. | +| | You can use else section to handle empty values. | `{{#with city}}...{{else}}No city found{{/with}}` | Handles empty context case. | # Novu-specific helpers -| Handlebar Expression | Description | -| --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `{{dateFormat date 'EEEE, MMMM Do yyyy'}}` | You can format the date using the `dateFormat` function. Here, `date: '2020-01-01'` has been formatted into `Wednesday, January 1st 2020`. | +| Handlebar Expression | Description | +| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `{{dateFormat date 'EEEE, MMMM Do yyyy'}}` | You can format the date using the `dateFormat` function. Here, `date: '2020-01-01'` has been formatted into `Wednesday, January 1st 2020`. | | `{{lowercase key}}` | This helps you use the `lowercase` handlebar helper function and turns the value of the specified key to its lowercase value. So, for `message: 'hEllo WORLD'`, if we write `{{lowercase message}}`, we'll end up with `hello world`. | | `{{uppercase key}}` | This helps you use the `uppercase` handlebar helper function and turns the value of the specified key to its uppercase value. So, for `message: 'hello woRld'`, if we write `{{uppercase message}}`, we'll end up with `HELLO WORLD`. | | `{{titlecase key}}` | This helps you use the `titlecase` handlebar helper function and turns the value of the specified key to its titlecase value. So, for `message: 'hEllo wOrLD'`, if we write `{{titlecase message}}`, we'll end up with `Hello World`. | ### groupBy + `groupBy` helper is used to group the items in an array based on a property. @@ -78,8 +78,8 @@ This resource offers comprehensive insights into the utilization of Handlebars.j - ### equals + `equals` helper allows you to perform comparisons, both for boolean expressions and string comparisons. @@ -92,9 +92,10 @@ This resource offers comprehensive insights into the utilization of Handlebars.j {{/equals}} - + {{#equals 10 1}} +

    10 is equal to 1

    {{else}}

    10 is not equal to 1

    @@ -117,6 +118,7 @@ This resource offers comprehensive insights into the utilization of Handlebars.j
    ### numberFormat + `numberFormat` helper is used to format numbers with specified decimal length, thousands separator, and decimal separator. @@ -143,6 +145,7 @@ This resource offers comprehensive insights into the utilization of Handlebars.j ### pluralize + The `pluralize` helper allows you to easily handle pluralization in your templates by providing different forms of a word depending on a variable value. ### Basic Usage @@ -190,7 +193,7 @@ In this code, we iterate through the `step.events` array and conditionally displ By using this code, you can achieve a more accurate representation of the event count in your messages, even when some users are individually mentioned in the message text. -### Using array length as count +### Using array length as count Array length be used as total number of item. For example, if paylod have fruits property of type array then its length can be used as total number of fruits. @@ -227,6 +230,10 @@ Total number of fruits is 3. - - In a future release, we plan to replace handlebar helpers with an alternative solution. Please note that custom handlebars in the editor itself are not currently supported. If we are missing any handlebar helper that is blocking you to write template for use case, feel free to reach out to us in [discord](https://discord.gg/novu?ref=docs-handlebar-helper) - + + {" "} + In a future release, we plan to replace handlebar helpers with an alternative solution. + Please note that custom handlebars in the editor itself are not currently supported. + If we are missing any handlebar helper that is blocking you to write template for + use case, feel free to reach out to us in [discord](https://discord.gg/novu?ref=docs-handlebar-helper) + diff --git a/content-creation-design/layouts.mdx b/content-creation-design/layouts.mdx index aac84e8f..84a9ec8f 100644 --- a/content-creation-design/layouts.mdx +++ b/content-creation-design/layouts.mdx @@ -41,29 +41,33 @@ To override your assigned layout during a trigger event, use the `layoutIdentifi The layout specified will be used for all emails in the context of that trigger event. ```ts -import { Novu } from '@novu/node'; +import { Novu } from "@novu/node"; -const novu = new Novu(''); +const novu = new Novu(""); -novu.trigger('workflow-identifier', { +novu.trigger("workflow-identifier", { to: { - subscriberId: '', + subscriberId: "", }, payload: { attachments: [ { - file: fs.readFileSync(__dirname + '/data/test.jpeg'), - name: 'test.jpeg', - mime: 'image/jpg', + file: fs.readFileSync(__dirname + "/data/test.jpeg"), + name: "test.jpeg", + mime: "image/jpg", }, ], }, overrides: { - layoutIdentifier: 'your-layout-identifier', + layoutIdentifier: "your-layout-identifier", }, }); ``` - The override layout on trigger functionality is only available on v0.19.0. + + + {" "} + The override layout on trigger functionality is only available on v0.19.0. + #### Using SDK @@ -171,4 +175,4 @@ const layouts = await novu.layouts.list({ ## API Reference Links -- [Layout Methods](/api-reference/layouts/layout-creation/) \ No newline at end of file +- [Layout Methods](/api-reference/layouts/layout-creation/) diff --git a/content-creation-design/notification-content-creation.mdx b/content-creation-design/notification-content-creation.mdx index 9bf1f27d..8d7132e5 100644 --- a/content-creation-design/notification-content-creation.mdx +++ b/content-creation-design/notification-content-creation.mdx @@ -8,18 +8,19 @@ To demonstrate the extensive range of possibilities, we've crafted a subscriber ```typescript const subscriber = { - subscriberId: '6427e97d0136cef86a315c46', - firstName: 'John', - lastName: 'Doe', - email: 'john.doe@gmail.com', - phone: '+98712345677', - avatar: 'https://sm.ign.com/ign_nordic/cover/a/avatar-gen/avatar-generations_prsz.jpg', - locale: 'en-US', - data: { - plan: 'basic', - zipcode: 14925, - validVehicleLicense: true - } + subscriberId: "6427e97d0136cef86a315c46", + firstName: "John", + lastName: "Doe", + email: "john.doe@gmail.com", + phone: "+98712345677", + avatar: + "https://sm.ign.com/ign_nordic/cover/a/avatar-gen/avatar-generations_prsz.jpg", + locale: "en-US", + data: { + plan: "basic", + zipcode: 14925, + validVehicleLicense: true, + }, }; ``` @@ -42,19 +43,12 @@ Example: Send a personalized welcome email to a user by leveraging their propert **Input:** ```html -Hello {{subscriber.firstName}} {{subscriber.lastName}}! - -We are excited to welcome you to our {{subscriber.data.plan}} plan. -Look forward to receiving daily updates from us on your phone number: {{subscriber.phone}}. - -{{#if subscriber.data.validVehicleLicense}} -Why not consider renting a car to visit us? -{{else}} -We're delighted to offer you a train ticket to come see us. -{{/if}} - -Should you require any assistance, don't hesitate to reach out. -Best regards, +Hello {{subscriber.firstName}} {{subscriber.lastName}}! We are excited to +welcome you to our {{subscriber.data.plan}} plan. Look forward to receiving +daily updates from us on your phone number: {{subscriber.phone}}. {{#if +subscriber.data.validVehicleLicense}} Why not consider renting a car to visit +us? {{else}} We're delighted to offer you a train ticket to come see us. {{/if}} +Should you require any assistance, don't hesitate to reach out. Best regards, Your friends at Novu. ``` @@ -66,16 +60,10 @@ Your friends at Novu. **Output:** ```html -Hello John Doe! - -We are excited to welcome you to our basic plan. -Look forward to receiving daily updates from us on your phone -number: +98712345677. - -Why not consider renting a car to visit us? -Should you require any assistance, don't hesitate to reach out. -Best regards, -Your friends at Novu. +Hello John Doe! We are excited to welcome you to our basic plan. Look forward to +receiving daily updates from us on your phone number: +98712345677. Why not +consider renting a car to visit us? Should you require any assistance, don't +hesitate to reach out. Best regards, Your friends at Novu. ``` -Email Subject, Sender Name and Preheader field accepts system, payload variables and handlebar helpers -[Explore how to manage Email layouts.](/content-creation-design/layouts) + + Email Subject, Sender Name and Preheader field accepts system, payload + variables and handlebar helpers + +[Explore how to manage Email layouts.](/content-creation-design/layouts){" "} ## In-App @@ -206,7 +197,9 @@ You have the ability to utilize all variables to craft dynamic and personalized /> ## Push + Push channel has two fields `Push Message Title` and `Push Message Content` where user can write hard coded text content or use system, dynamic variables or handlebar helpers to customize notification content. + The feature is only available for **Novu Cloud** users with **Business** or **Enterprise** plans. + + The feature is only available for **Novu Cloud** users with **Business** or + **Enterprise** plans. + ## Introduction @@ -23,21 +26,33 @@ Translation Group allows you to efficiently manage translations for diverse lang To create the Translation Group navigate to the `Translations` page from the main navigation and click on the “Add group” button. - + Before creating your first Translation Group you'll be prompted to specify the default locale for organization. This locale serves as a fallback option for localized messages in cases where the recipient hasn't defined a specific locale. - + After selecting the default language, you can provide the essential details: the group name, group identifier, and select your target languages. The group identifier is an important value that you will later use in the editor to localize your messages. This is a similar concept that we use across the app to identify the main parts. - + After the group is created, you will land on the group details page on which you can see the list with the empty translations for the languages you choose. - + ## Translations @@ -67,15 +82,24 @@ Translations are JSON files that contain the content of your messages in a speci To upload your translations click on the “Upload files” button located at the top of the Translation Group details page. - + Select the translation files and click “Open” button. - + With each uploaded translation file, you must indicate the language it references. Additionally, you can verify the correctness of the uploaded file by previewing its contents. - + Click on “Upload files” button. Congratulations you just uploaded the translation files and created the translation group! 🎉 @@ -88,9 +112,15 @@ Let’s take a look at the example: `{{i18n "marketing.welcome_message"}}`. - `i18n` is the handlebar helper that tells Novu that you would like to use the Translations - `"marketing.welcome_message"` is the path that is composed of the Translation Group identifier `marketing` and translation JSON file key `welcome_message` - + -i18n variables can be used in `subject` and `preheader` fields of the email step. + + i18n variables can be used in `subject` and `preheader` fields of the email + step. + ## Subscribers Locale The subscribers locale defines in which language the message should be delivered to that recipient. When the locale is not specified it will fallback to the default locale set for the organization. The locale format includes ISO language plus the region, for example `de_DE` language German and region Germany. @@ -123,9 +153,9 @@ For example: ```json { - "friend": "A friend", - "friend_male": "A boyfriend", - "friend_female": "A girlfriend" + "friend": "A friend", + "friend_male": "A boyfriend", + "friend_female": "A girlfriend" } ``` @@ -143,8 +173,8 @@ To distinguish between plural translation options. ```json { - "keyWithCount": "{{count}} item", - "keyWithCount_plural": "{{count}} items", + "keyWithCount": "{{count}} item", + "keyWithCount_plural": "{{count}} items" } ``` diff --git a/content-creation-design/variables.mdx b/content-creation-design/variables.mdx index 82f0c6cf..143d4a5b 100644 --- a/content-creation-design/variables.mdx +++ b/content-creation-design/variables.mdx @@ -1,18 +1,18 @@ --- -title: 'Workflow Editor' -description: '' -icon: 'code' +title: "Workflow Editor" +description: "" +icon: "code" --- -Novu's content creation tools allow developers to utilize a wealth of variables when designing notification workflows. +Novu's content creation tools allow developers to utilize a wealth of variables when designing notification workflows. As developers construct workflows using Novu, certain status facets—results of batch functions and other steps in the workflow—are auto-generated and can be utilized to control the content displayed in notification templates. -These variables offer dynamic and automated properties that hold subscriber information, such as first and last name, email, phone, and avatar. +These variables offer dynamic and automated properties that hold subscriber information, such as first and last name, email, phone, and avatar. For example, developers can insert a subscriber's first name into a workflow by using `{{ subscriber.firstName }}`. -In addition to the subscriber's details, developers can use data payload variables that encapsulate dynamic data meant to be injected into the content of a workflow. +In addition to the subscriber's details, developers can use data payload variables that encapsulate dynamic data meant to be injected into the content of a workflow. For instance, in the code example the variables username and resetLink are data payload variables that would be adjusted based on the workflow's purpose. @@ -20,59 +20,58 @@ For instance, in the code example the variables username and resetLink are data ## System Variables - ### Subscriber Variables -| Variable | Type | Description | -| ------------- | -------- | ------------------------------------------------------ | -| `subscriber` | `Object` | Represents someone who is intended to receive a notification. | -| `firstName` | `String` | The first name of the subscriber. | -| `lastName` | `String` | The last name of the subscriber. | -| `email` | `String` | The subscriber's email address. | -| `phone` | `String` | The phone number associated with the subscriber. | -| `avatar` | `String` | A URL or reference to the subscriber’s avatar or profile picture. | -| `locale` | `String` | The preferred locale or language of the subscriber. | -| `subscriberId` | `String` | Unique identifier for the subscriber. | -| `data` | `Object` | An additional object that can hold custom subscriber data as key-value pairs. | +| Variable | Type | Description | +| -------------- | -------- | ----------------------------------------------------------------------------- | +| `subscriber` | `Object` | Represents someone who is intended to receive a notification. | +| `firstName` | `String` | The first name of the subscriber. | +| `lastName` | `String` | The last name of the subscriber. | +| `email` | `String` | The subscriber's email address. | +| `phone` | `String` | The phone number associated with the subscriber. | +| `avatar` | `String` | A URL or reference to the subscriber’s avatar or profile picture. | +| `locale` | `String` | The preferred locale or language of the subscriber. | +| `subscriberId` | `String` | Unique identifier for the subscriber. | +| `data` | `Object` | An additional object that can hold custom subscriber data as key-value pairs. | ### Actor Variables -| Variable | Type | Description | -| ------------- | -------- | ------------------------------------------------------ | -| `actor` | `Object` | A subscriber who initiates actions triggering events. | -| `firstName` | `String` | The first name of the actor. | -| `lastName` | `String` | The last name of the actor. | -| `email` | `String` | The actor's email address. | -| `phone` | `String` | The phone number associated with the actor. | -| `avatar` | `String` | A URL or reference to the actor’s avatar or profile picture. | -| `locale` | `String` | The preferred locale or language of the actor. | -| `subscriberId` | `String` | Unique identifier for the actor. | -| `data` | `Object` | An additional object that can hold custom actor data as key-value pairs. | +| Variable | Type | Description | +| -------------- | -------- | ------------------------------------------------------------------------ | +| `actor` | `Object` | A subscriber who initiates actions triggering events. | +| `firstName` | `String` | The first name of the actor. | +| `lastName` | `String` | The last name of the actor. | +| `email` | `String` | The actor's email address. | +| `phone` | `String` | The phone number associated with the actor. | +| `avatar` | `String` | A URL or reference to the actor’s avatar or profile picture. | +| `locale` | `String` | The preferred locale or language of the actor. | +| `subscriberId` | `String` | Unique identifier for the actor. | +| `data` | `Object` | An additional object that can hold custom actor data as key-value pairs. | ### Step Variables -| Variable | Type | Description | -| ------------- | -------- | ------------------------------------------------------ | -| `step` | `Object` | A component in the execution of a workflow. | -| `digest` | `Boolean` | Indicates whether digest mode is active or not. | -| `events` | `Array` | An aggregated collection of events (that are stored when digest is active). | -| `total_count` | `Number` | Represents the total number of events in the digest. | +| Variable | Type | Description | +| ------------- | --------- | --------------------------------------------------------------------------- | +| `step` | `Object` | A component in the execution of a workflow. | +| `digest` | `Boolean` | Indicates whether digest mode is active or not. | +| `events` | `Array` | An aggregated collection of events (that are stored when digest is active). | +| `total_count` | `Number` | Represents the total number of events in the digest. | ### Brand Variables -| Variable | Type | Description | -| --------- | -------- | ------------------------------------------------------ | -| `branding` | `Object` | Enables customization of the notification's visual identity. | -| `logo` | `String` | Insert the brand logo. | +| Variable | Type | Description | +| ---------- | -------- | --------------------------------------------------------------------------- | +| `branding` | `Object` | Enables customization of the notification's visual identity. | +| `logo` | `String` | Insert the brand logo. | | `color` | `String` | Set the primary color of the notification based on the brand configuration. | ### Tenant Variables -| Variable | Type | Description | -| --------- | -------- | ------------------------------------------------------ | -| `tenant` | `Object` | Group of users or an organization. | -| `name` | `String` | Insert the brand logo. | -| `data` | `Object` | An additional object that can hold custom tenant data as key-value pairs. | +| Variable | Type | Description | +| -------- | -------- | ------------------------------------------------------------------------- | +| `tenant` | `Object` | Group of users or an organization. | +| `name` | `String` | Insert the brand logo. | +| `data` | `Object` | An additional object that can hold custom tenant data as key-value pairs. | ## Data Payload Variables @@ -98,8 +97,8 @@ curl -L -X POST 'https://api.novu.co/v1/events/trigger' \ }' ``` -Within this code snippet, the variables `username` and `resetLink` are representative of data payload variables. +Within this code snippet, the variables `username` and `resetLink` are representative of data payload variables. Importantly, it's worth noting that the payload itself can encompass any serializable JSON object. -Avoid use of system reserved variables as normal variables. For example don't use `email` as variable name because `{{subscriber.email}}` is a system reserved variable \ No newline at end of file +Avoid use of system reserved variables as normal variables. For example don't use `email` as variable name because `{{subscriber.email}}` is a system reserved variable diff --git a/framework/deployment/actions.mdx b/deployment/actions.mdx similarity index 100% rename from framework/deployment/actions.mdx rename to deployment/actions.mdx diff --git a/framework/deployment/cli.mdx b/deployment/cli.mdx similarity index 100% rename from framework/deployment/cli.mdx rename to deployment/cli.mdx diff --git a/deployment/production.mdx b/deployment/production.mdx new file mode 100644 index 00000000..bff043de --- /dev/null +++ b/deployment/production.mdx @@ -0,0 +1,73 @@ +--- +title: "Going to Production" +--- + +Novu operates in a multi environment setup. Novu currently supports 2 environments: + +- **Development** - For creating, editing, and previewing workflows. +- **Production** - For triggering workflows to your customers. + +## Sync changes to Novu Cloud + +Novu Framework operates in a GitOps model. This means that the source of truth for your workflows and configurations are located in your Git as Code. + +The general workflow for pushing changes to Novu Cloud is as follows: + +- Create a feature branch +- Develop workflows locally in your bridge application +- Sync changes to the Development environment to test e2e +- Merge the feature branch to your `dev` branch + - This will trigger a CI/CD pipeline that will deploy the changes to the Development environment +- Test the changes in the Development environment +- Merge the `dev` branch to the `main` branch + - This will trigger a CI/CD pipeline that will deploy the changes to the Production environment + +## Networking + +Novu Cloud workers will need to be able to communicate with your [Bridge Endpoint](/concepts/endpoint). You will need to ensure that your firewall rules allow traffic from the internet. +Due to the autoscaling nature of Novu Cloud, we don't have a set of IP Addresses that you can whitelist. + +## Security + +Novu Cloud workers are GDPR, SOC2 type II and ISO 27001 compliant. We take security very seriously and have implemented a number of security measures to ensure that your data is safe. +Novu Framework has a builtin security mechanism that ensures that the requests are authentic from Novu Cloud using an HMAC signature. + +HMAC Verification is turned on by default + +```typescript +export const client = new Client({ + secretKey: process.env.NOVU_SECRET_KEY, + strictAuthentication: process.env.NODE_ENV !== "development", // set to true by default +}); +``` + + + For local development with Studio `strictAuthentication` should be set to + `false`. + + +The `X-Novu-Signature` header included in each signed event contains a timestamp and one or more signatures that we verify. +The timestamp is prefixed by `t=`, and each signature is prefixed by a scheme. +Schemes start with `v`, followed by an integer. Currently, the only valid live signature scheme is v1. + +{/* todo add an example of the x-novu-signature header here */} + +Handling the signature verification is done by the Novu Framework, so you don't need to perform any action. + +## CI/CD Integrations + +Novu currently supports the following CI integrations: + +- **GitHub Actions** - [Direct Integration](/deployment/actions) +- **GitLab CI** - Using our [CLI command](/deployment/cli) +- **Jenkins** - Using our [CLI command](/deployment/cli) +- **CircleCI** - Using our [CLI command](/deployment/cli) +- **Bitbucket Pipelines** - Using our [CLI command](/deployment/cli) +- **Azure DevOps** - Using our [CLI command](/deployment/cli) +- **Travis CI** - Using our [CLI command](/deployment/cli) +- **Other** - For any other CI/CD tool, you can use our [CLI command](/deployment/cli) + + + Direct integration with other CI/CD tools is on our roadmap. If you would like + to see a specific CI/CD tool integrated, please reach out to us. + diff --git a/echo-terminal.md b/echo-terminal.md index 39c84b3f..e8049321 100644 --- a/echo-terminal.md +++ b/echo-terminal.md @@ -11,11 +11,22 @@ The JSDelivr URLs below point to the latest commit in the main branch, ensuring The Echo terminal can be loaded via both JS and HTML. ### Loading with Javascript (preferred) + Use this method if you have access to custom JS. ```javascript // Custom JS injection -!function(e){if(!document.getElementById(e)){var t=document.createElement("script");t.src="https://cdn.jsdelivr.net/gh/novuhq/docs/echo-terminal.min.js",t.type="text/javascript",t.crossOrigin="anonymous",t.id=e;var n=document.getElementsByTagName("script")[0];n?n.parentNode.insertBefore(t,n):document.body.appendChild(t)}}("nv-echo-terminal-loader"); +!(function (e) { + if (!document.getElementById(e)) { + var t = document.createElement("script"); + (t.src = "https://cdn.jsdelivr.net/gh/novuhq/docs/echo-terminal.min.js"), + (t.type = "text/javascript"), + (t.crossOrigin = "anonymous"), + (t.id = e); + var n = document.getElementsByTagName("script")[0]; + n ? n.parentNode.insertBefore(t, n) : document.body.appendChild(t); + } +})("nv-echo-terminal-loader"); ``` ```html @@ -24,11 +35,17 @@ Use this method if you have access to custom JS. ``` ### Loading with HTML + Use this method if you only have access to modify the `` element. ```html - + diff --git a/framework/concepts/endpoint.mdx b/framework/concepts/endpoint.mdx deleted file mode 100644 index 758fb7f2..00000000 --- a/framework/concepts/endpoint.mdx +++ /dev/null @@ -1,68 +0,0 @@ ---- -title: "Bridge Endpoint" ---- - -## Bridge Endpoint - -Novu Framework requires a **single** `HTTP` endpoint (`/api/novu` or similar) to be exposed by your application. This endpoint is used to receive events from our Worker Engine. - -You can view the Bridge Endpoint as a webhook endpoint that Novu will call when it needs to retrieve contextual information for a given subscriber and notification. - -Using the `create-novu-app` command creates a Bridge application for you with a Bridge endpoint ready to go. - -## The `serve` function - -We offer framework specific wrappers in form of an exported `serve` function that abstracts away: - -- Parsing the incoming request for `GET`, `POST`, `PUT` and `OPTIONS` requests -- HMAC header authentication -- Framework specific response and error handling - -Currently, we offer `serve` functions for the following frameworks: -- [Next.js](/framework/sdk/frameworks/nextjs) -- [Express.js](/framework/sdk/frameworks/express) -- [Nuxt](/framework/sdk/frameworks/nuxt) -- [h3](/framework/sdk/frameworks/h3) -- [Remix](/framework/sdk/frameworks/remix) -- [Sveltekit](/framework/sdk/frameworks/sveltekit) - -## Writing a custom `serve` function - -If we currently don't support your framework, you can write a custom `serve` function like the following example: - -```ts -import { type Request, type Response } from 'express'; -import { NovuRequestHandler, ServeHandlerOptions } from '@novu/framework'; - -export const serve = (options: ServeHandlerOptions): any => { - const requestHandler = new NovuRequestHandler({ - frameworkName: 'express', - ...options, - handler: ( - incomingRequest: Request, - response: Response, - ) => ({ - method: () => incomingRequest.method, - headers: (key) => { - const header = incomingRequest.headers[key]; - return Array.isArray(header) ? header[0] : header; - }, - queryString: (key) => { - const qs = incomingRequest.query[key]; - return Array.isArray(qs) ? qs[0] : qs; - }, - body: () => incomingRequest.body, - url: () => new URL(incomingRequest.url, `https://${incomingRequest.headers.get("host") || ""}`), - transformResponse: ({ body, headers, status }) => { - Object.entries(headers).forEach(([headerName, headerValue]) => { - response.setHeader(headerName, headerValue); - }); - - return response.status(status).send(body); - }, - }), - }); - - return requestHandler.createHandler(); -}; -``` diff --git a/framework/concepts/inputs.mdx b/framework/concepts/inputs.mdx deleted file mode 100644 index d3469a88..00000000 --- a/framework/concepts/inputs.mdx +++ /dev/null @@ -1,77 +0,0 @@ ---- -title: "Step Inputs" -sidebarTitle: "Step Inputs" ---- - -Inputs are defined using [JSON Schema](/framework/recipes/json-schema), providing a strong run-time validation system for your workflows. - -This ensures that you as the developer, and your non-technical peers are speaking the same language. -Those responsible for styling and copy can edit with confidence, knowing their changes are tested in code. - -## Inputs vs Payload - -**Inputs** - For Non-Technical Peers and Developers. Managed in the Novu Web UI, defined by developers and used by non-technical peers. - - **Examples** - Color of a button, text of a button, should a section be shown, digest duration, static text content, the order of the email sections, and etc... - - -**Payload Schema** - For Developers. Passed during the `novu.trigger` method, and controlled by the developer. - - **Examples** - User ID, Post ID, Comment, Order ID, 2FA token and etc... - - -## Step Inputs - -[Step](/framework/concepts/steps) input schema is defining the input passed during the `step` method. Those inputs can be modified and persisted in the Novu Web UI. - -The snippet below shows configuration for the Workflow payload schema and the Step input schema. If you don’t provide a schema, Typescript will infer the data type to `unknown`, reminding you of the best practice to specify your schema. - -```tsx -workflow('new-signup', async ({ step, payload }) => { - await step.email( - 'send-email', - async (inputs) => { - return { - subject: inputs.subjectTitle, - body: render(), - } - }, { - // Learn about JSON Schema here: https://json-schema.org/specification - inputSchema: { - // Always `object` - type: 'object', - // Specify the properties to validate. Supports deep nesting. - properties: { - hideBanner: { type: 'boolean', default: false }, - subjectTitle: { type: 'string' }, - // Allowing no code control over the component in the Web UI - components: { - type: "array", - items: { - type: "string" - }, - default: ["header", "cta-row", "footer"] - } - }, - // Specify the array of which properties are required. - required: ['complianceFooter'], - // Used to enforce full type strictness, with no rogue properties. - additionalProperties: false, - // The `as const` is important to let Typescript know that this - // type won't change, enabling strong typing on `inputs` via type - // inference of the provided JSON Schema. - } as const, - }); -}); -``` - -Learn more about [JSON Schema here](https://json-schema.org/specification), including the powerful validation features to ensure your Content renders perfectly, every time. - - - Other validators, including Zod and more, coming soon. - Any validator that supports transformation to JSON Schema can be added. - - -## JSON Schema Examples - -To Learn more about advanced examples, visit our [JSON Schema](/framework/recipes/json-schema) page. diff --git a/framework/concepts/payload.mdx b/framework/concepts/payload.mdx deleted file mode 100644 index 255c401c..00000000 --- a/framework/concepts/payload.mdx +++ /dev/null @@ -1,57 +0,0 @@ ---- -title: "Payload" ---- - -Workflow payload is the data passed during the `novu.trigger` method. This is useful for ensuring that the payload is correctly formatted and that the data is valid. - -## Payload Schema - -Payload schema is defining the payload passed during the `novu.trigger` method. This is useful for ensuring that the payload is correctly formatted and that the data is valid. - -```tsx -workflow('comment-on-post', async ({ step, payload }) => { - await step.email( - 'send-email', - async () => { - return { - subject: `You have a new comment from: ${payload.author_name}.`, - body: render(), - } - }); - }, - { - payloadSchema: { - // Always `object` - type: 'object', - // Specify the properties to validate. Supports deep nesting. - properties: { - post_id: { type: 'number' }, - author_name: { type: 'string' }, - comment: { type: 'string', maxLength: 200 }, - }, - // Specify the array of which properties are required. - required: ['post_id', 'comment'], - // Used to enforce full type strictness, with no rogue properties. - additionalProperties: false, - // The `as const` is important to let Typescript know that this - // type won't change, enabling strong typing on `inputs` via type - // inference of the provided JSON Schema. - } as const, - }, -); -``` - -## Passing Payload - -Here is an example of the validated payload during trigger: - -```tsx -novu.trigger('comment-on-post', { - to: 'subscriber_id', - payload: { - post_id: 1234, - author_name: 'John Doe', - comment: 'Looks good!' - } -}); -``` diff --git a/framework/concepts/studio.mdx b/framework/concepts/studio.mdx deleted file mode 100644 index fe9efdbd..00000000 --- a/framework/concepts/studio.mdx +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: "Dev Studio" ---- - - -The Echo Dev Studio is a companion app to the Echo Client SDK. Its goal is to provide a local environment that lives near your code. - -To launch the dev studio locally you can run: `npx novu-labs@latest echo` . The Dev Studio will be started by default on port 2022, and accessible via: `http://localhost:2022` - -After successfully connecting the Studio to your local edge endpoint, you will be able to preview in real time any workflows and content defined in your code. This is ideal for quick prototyping, debugging, styling, and adjusting your workflows before syncing them to Novu Cloud. - -### Inputs and Payload forms - -You can quickly modify the step inputs and workflow payload to preview your workflow's different states and be generated with. This is helpful to quickly debug how the email will behave in case of a missing input, or iterate more complex content structures. - -### Syncing State - -Sync state to your Production or Development environment in Novu, is recommended to do via your CI pipeline. However, a sync can be made using the Dev Studio for quick experimentation. - -Click on the `Sync` button at the top right corner of the navigation bar, and a Diff will be generated between your local and the Novu Cloud environments. - -After inspecting the State Diff, and specifying the remote `Echo Url` Click on the `Deploy Changes` button. - -### **Debugging locally** - -To allow Novu to communicate with your local machine a tunnel will need to be generated. The quickest way to do it is with `localtunnel.` To Create a new tunnel in your terminal, type: `npx localtunnel --port ` . This command will return an https endpoint, which then can be used during the sync state flow described earlier. diff --git a/framework/concepts/workflows.mdx b/framework/concepts/workflows.mdx deleted file mode 100644 index 7beb8de0..00000000 --- a/framework/concepts/workflows.mdx +++ /dev/null @@ -1,145 +0,0 @@ ---- -title: "Workflows" -description: "Workflows are at the core of responding to events in your system with notifications. Novu Framework enables you to declare type-safe, validated, and version-controlled Novu Workflows with code." ---- - -## Introduction -The most basic Workflow to send a notification in response to an event looks like: - -```tsx -import { workflow } from '@novu/framework'; - -const myWorkflow = workflow('new-signup', async ({ step, payload }) => { - // Send a welcome email - await step.email('send-email', async () => { - return { - subject: `Welcome to Acme, ${payload.name}`, - body: 'We look forward to helping you achieve mission.', - } - }); - // JSON Schema for validation and type-safety. Zod, and others coming soon. -}, { payloadSchema: { properties: { name: { type: 'string' }}}} ); -``` - -## Just-in-time data fetching -You can add any custom logic into your steps that you need. Maybe you’d like to fetch some information about your new sign-up from somewhere else during the Workflow execution. You can achieve it with the following changes: - -```tsx -const myWorkflow = workflow('new-signup', async ({ step, payload }) => { - // Send a welcome email - await step.email('send-email', async () => { - // Fetch the user from your database - const user = await db.getUser(payload.userId); - return { - subject: `Welcome to Acme ${user.productTier} tier, ${user.name}.`, - // 'Welcome to Acme Business tier, John Doe.' - body: 'We look forward to helping you achieve mission.', - } - }); - // Changing the schema from `name` -> `userId`, for type-safety. - }, { payloadSchema: { properties: { userId: { type: 'string' }}}} ); -``` - -We call this **just-in-time** notification data fetching. It allows you pull in data from the relevant sources during the Workflow execution, removing the need to store all of your subscriber data in Novu. - -## Multi-step workflow -Now, what if you wanted to send another update to the same new user in 1 week? But, you don't want to send the follow-up if the User opted out. We can add more steps to the Workflow to achieve this. - -```tsx -const myWorkflow = workflow( - 'new-signup', - async ({ step, payload }) => { - await step.email('send-email', async () => { - const user = await db.getUser(payload.userId); - return { - subject: `Welcome to Acme ${user.productTier} tier, ${user.name}.`, - body: 'We look forward to helping you achieve mission.', - }; - }); - - // Wait for 1 week before continuing. After 1 week, Novu will continue - // executing the Workflow from here. - await step.delay('onboarding-follow-up', async () => ({ - amount: 1, - type: 'regular', - unit: 'weeks', - })); - - // 1 week passed, let's follow up with an in-app notification. - await step.inApp( - 'onboarding-follow-up', - async (inputs) => { - const user = await db.getUser(payload.userId); - // The `feedbackUrl` can be updated in Novu Web without changing code. - // This helps you to create re-usable Workflow snippets. - return { - body: `Hey ${user.name}! How do you like the product? - - Let us know here - if you have any questions.`, - }; - }, - { - // Don't follow up if the Subscriber opted out. - skip: () => !payload.shouldFollowUp, - // Add validation to ensure `feedbackUrl` provided in Web is a `uri` - inputSchema: { - type: 'object', - properties: { feedbackUrl: { type: 'string', format: 'uri', default: 'https://acme.com/feedback' } }, - required: ['feedbackUrl'], - additionalProperties: false, - } as const, - }, - ); - }, - { - payloadSchema: { - type: 'object', - properties: { - userId: { type: 'string' }, - shouldFollowUp: { type: 'boolean', default: true }, - }, - required: ['userId', 'shouldFollowUp'], - additionalProperties: false, - } as const, - }, -); -``` - -With this simple Workflow, we: - -- Sent a new signup email -- Waited 1 week -- Sent an in-app follow-up notification - -You should now have a grasp of the flexibility that Novu Framework offers. - -Continue your reading in [Steps](/framework/concepts/steps) to find out the other channels and actions you can use with Novu Framework. - -## Workflow Interface - -```tsx -workflow( - // the Workflow name. Must be unique across your Bridge server. - 'new-signup', - // Workflow resolver, the entry-point for your Workflow steps - async ({ - // Helper function to declare your Workflow Steps. - step, - // Workflow Trigger payload - payload, - }) => { - // ...your Workflow Steps - -}, { - // JSON Schema for validation and type-safety. Zod, and others coming soon. - // https://json-schema.org/draft-07/json-schema-release-notes - - // The schema for the Workflow payload passed dynamically via Novu Trigger API - // Defaults to an empty schema. - payloadSchema: { properties: { name: { type: 'string' }}}, - // The schema for the Workflow inputs passed statically via Novu Web - // Defaults to an empty schema. - inputSchema: { properties: { brandColor: { type: 'string' }}}, -}); -``` diff --git a/framework/deployment/production.mdx b/framework/deployment/production.mdx deleted file mode 100644 index befc1d5b..00000000 --- a/framework/deployment/production.mdx +++ /dev/null @@ -1,68 +0,0 @@ ---- -title: "Going to Production" ---- - -Novu operates in a multi environment setup. Currently Novu Supports 2 environments: - -- **Development** - This is the environment where the development team works on workflows. -- **Production** - This is the environment that is used to trigger workflows to your customers. - -## Pushing changes to Cloud - -Novu Framework operates in a GitOps model. This means that the source of truth for your workflows and configurations are located in your Git as Code. - -The general workflow for pushing changes to Cloud is as follows: -- Create a feature branch -- Develop workflows locally in your Bridge application -- Sync changes to the Development environment to test e2e -- Merge the feature branch to your `dev` branch - - This will trigger a CI/CD pipeline that will deploy the changes to the Development environment -- Test the changes in the Development environment -- Merge the `dev` branch to the `main` branch - - This will trigger a CI/CD pipeline that will deploy the changes to the Production environment - -## Networking - -Novu Cloud workers will need to be able to communicated with your Bridge Endpoint. You will need to ensure that your firewall rules allow traffic from the internet. -Due to the autoscaling nature of Novu Cloud, we don't have a set of IP Addresses that you can whitelist. - -## Security - -Novu Cloud workers are GDPR, SOC2 type II and ISO 27001 compliant. We take security very seriously and have implemented a number of security measures to ensure that your data is safe. -Novu Framework has a builtin security mechanism that ensures that the requests are authentic from Novu Cloud using an HMAC signature. - -HMAC Verification is turned on by default - -```typescript -export const client = new Client({ - apiKey: process.env.NOVU_API_KEY,, - strictAuthentication: process.env.NODE_ENV !== "development", // set to true by default -}); -``` - - - For local development with Studio `strictAuthentication` should be set to `false`. - - -The `X-Novu-Signature` header included in each signed event contains a timestamp and one or more signatures that we verify. -The timestamp is prefixed by t=, and each signature is prefixed by a scheme. -Schemes start with v, followed by an integer. Currently, the only valid live signature scheme is v1. - -Handling the signature verification is done by the Novu Framework, you don't need to perform any action. - -## CI/CD Integrations - -Novu currently supports the following CI integrations: -- **GitHub Actions** - [Direct Integration](/framework/deployment/actions) -- **GitLab CI** - Using our [CLI command](/framework/deployment/cli) -- **Jenkins** - Using our [CLI command](/framework/deployment/cli) -- **CircleCI** - Using our [CLI command](/framework/deployment/cli) -- **Bitbucket Pipelines** - Using our [CLI command](/framework/deployment/cli) -- **Azure DevOps** - Using our [CLI command](/framework/deployment/cli) -- **Travis CI** - Using our [CLI command](/framework/deployment/cli) -- **Other** - For any other CI/CD tool, you can use our [CLI command](/framework/deployment/cli) - - - Direct integration with other CI/CD tools is on our roadmap. If you would like to see a specific CI/CD tool integrated, please reach out to us. - - diff --git a/framework/non-developers/introduction.mdx b/framework/non-developers/introduction.mdx deleted file mode 100644 index 6ef1b3f4..00000000 --- a/framework/non-developers/introduction.mdx +++ /dev/null @@ -1,40 +0,0 @@ ---- -title: "No-Code Tools to control your Notification Flows" -sidebarTitle: "Introduction" -description: "Change notification content and behavior with confidence using Novu Web UI." ---- - -Novu Framework was designed to bridge the gap between developers and on-developers in the team. - -- **Developers** - Define and abstract away complex logic, data manipulation and hydration, html and etc... -- **Non-Developers** - Use the Novu Web UI to control the content and behavior of the notifications using [Inputs](/framework/concepts/inputs). - -## How to Modify Inputs? - -Inputs are modified directly on the [Novu Web UI](https://web.novu.co). Each Workflow editor page have an 'Inputs' tab where you can modify them. -When Saving the changes, the new Input values will be used in the notification sent to your subscriber. - -## Common Input Usecases - -### Change Notification Content - -An input can be as broad as `content` or as specific as `2fa_code_title_color`. This allows you to change the content of the notification without needing to touch the code. - - - Currently you cannot use Dynamic payload variables as part of the Input. Reach out to us if you need this capability. - - -### Change Digest or Delay Frequency - -Inputs can be used for controlling any aspect of the step configuration, for example: - -- Digest Length -- Delay Amount -- Digest Type (Daily, Weekly, Monthly) -- etc... - -### Control structure and layout - -Inputs can be used to show/hide or rearrange the layout of the notification. -For example, you can have an input to show/hide a button, or change the position of a specific email section. - diff --git a/framework/quickstart.mdx b/framework/quickstart.mdx deleted file mode 100644 index 71018bb9..00000000 --- a/framework/quickstart.mdx +++ /dev/null @@ -1,60 +0,0 @@ ---- -title: "Quick Start" -description: "Novu Framework, the Code-first notification workflow engine. Build advanced workflows while giving your non-technical teammates complete control over content and behavior." ---- - -import { EchoTerminal } from '/snippets/echo-terminal.mdx'; - - - **Novu Framework is currently in public Alpha.** Need help? Have a suggestion? Join our [Discord community](https://discord.gg/novu). - - -## Introduction to Novu Framework - -Novu Framework allows you to write notification workflows in your codebase. Workflows are functions that execute business logic and use your preferred libraries for email, SMS, and chat generation. You can use Novu Framework with [React.Email](https://react.email/), [MJML](https://mjml.io/), or any other template generator. - - - -## Try it now - -To create a new Bridge application that will allow you to define your workflow in code, open your terminal and execute the `create-novu-app` command. - -```shell -npx create-novu-app@latest --api-key= -``` - -The command will ask you if you wish to install from a React.Email template or a clean project. - -## Core features - - - - Write the notification workflows as functions in your codebase, version, and manage them using Git. - - - Use React.email, MJML, or fetch templates from Braze, Hubspot, Sendgrid, and more... - - - Bring your own schemas for full, end-to-end validation and type safety. - - - Sync your workflows with Novu Cloud and ease collaboration. - - - - -## How it Works? - -Novu Framework introduces a new paradigm when working with Novu. Rather than defining workflows and templates at Novu Cloud, Novu Framework enables you to work in your application, use your own libraries and connect to your production environment during workflow execution over HTTPS. - -The Novu Framework exports the client you use to define and serve your workflows and their steps over HTTPS. The workflows can be added to your preferred framework. During workflow execution Novu Cloud makes HTTPS requests to your Bridge server for *just-in-time* Content and Subscribers resolution. - - - - - -## For Non-Technical Users - -Novu Framework was built with non-technical user in mind. Engineers can write complex workflows and expose a type-safe [Inputs](/framework/concepts/inputs) interface powered by JSON-Schema. -This enables Non-Technical teammates to safely modify the workflow without breaking mission critical notifications. - diff --git a/framework/roadmap.mdx b/framework/roadmap.mdx deleted file mode 100644 index 10d9a065..00000000 --- a/framework/roadmap.mdx +++ /dev/null @@ -1,25 +0,0 @@ ---- -title: "Roadmap" ---- - -This document outlines the roadmap for the Novu Framework project. It is a living document and will be updated as the project progresses. - -Our goal with Novu Framework is to create a Code-First but Product in-mind. Here you can see parts of our roadmap and future plans for it: - -Looking to add a feature request or upvote and existing feature? Head over to our [Public Roadmap](https://roadmap.novu.co). - -## Novu Framework, Now and Next -- ✅ **Workflows**: the ability to create multi-step workflows and manage them using Code. -- ✅ **CLI**: a command-line interface to interact with Novu Framework. -- ✅ **Studio**: a visual front to preview your local work. -- ✅ **Step Inputs**: JSON-Schema based interface for not technical users to modify the workflow content and behaviour. -- ✅ **GitHub Actions**: sync your workflows using GitHub Actions. -- ✅ **In App Seen/Read**: the ability to see the status of the in-app message. -- ✅ **Custom Step**: the ability to create custom and execute custom code in the workflow. For example: custom provider, retrieving database information, and 3rd party service integration. -- 🔜 **Zod Schema**: the ability to define the schema for the workflow and step inputs using Zod. -- 🔜 **Define Integrations**: code-first approach of creating and managing your provider integrations -- 🔜 **Email Reply Hook**: executing a custom logic code when a user replies to the email. -- 🔜 **Webhook**: receive webhooks about the execution flow of a workflow in Novu Framework. -- 🔜 **Eslint Plugin**: follow best practices and avoid common mistakes when writing code for Novu Framework. -- 🔜 **Codegen**: use our community driven list of workflow templates to use out of the box. - diff --git a/framework/sdk/client.mdx b/framework/sdk/client.mdx deleted file mode 100644 index 267c795d..00000000 --- a/framework/sdk/client.mdx +++ /dev/null @@ -1,36 +0,0 @@ ---- -title: "Introduction" ---- - -See the snippet below to learn how to configure your Bridge endpoint application. - -```tsx -import { Client } from '@novu/framework'; - -const client = new Client({ - /** - * Specify a custom Novu API URL. - * Defaults to 'https://api.novu.co', for EU use: https://eu.api.novu.co - */ - apiUrl: 'https://api.novu.co', - /** - * Specify your Novu API key, to secure your Bridge API endpoint. - * Novu communicates securely with your endpoint using a signed HMAC header, - * ensuring that only trusted requests from Novu are actioned by your Bridge API. - * The API key is used to sign the HMAC header. - */ - apiKey: process.env.NOVU_API_KEY, - /** - * Explicitly use HMAC signature verification. - * Setting this to `false` will enable Novu to communicate with your Bridge API - * without requiring a valid HMAC signature. - * This is useful for local development and testing. - * - * You are strongly encouraged to specify an `apiKey` and set this to `true` in production, - * to ensure that only trusted requests from Novu are actioned by your Bridge API. - * - * Defaults to true. - */ - strictAuthentication: process.env.NODE_ENV !== "development", -}); -``` diff --git a/framework/sdk/frameworks/express.mdx b/framework/sdk/frameworks/express.mdx deleted file mode 100644 index a68d3c14..00000000 --- a/framework/sdk/frameworks/express.mdx +++ /dev/null @@ -1,15 +0,0 @@ ---- -title: "Integrate Novu with Express.js" -sidebarTitle: "Express" ---- - -```tsx -import { serve } from "@novu/framework/express"; -import { myWorkflow } from "../novu/workflows"; - -app.use(express.json()); // Required for Novu POST requests -app.use( - "/api/novu", - serve({ workflows: [myWorkflow] }) -); -``` diff --git a/framework/sdk/frameworks/h3.mdx b/framework/sdk/frameworks/h3.mdx deleted file mode 100644 index ba1fd866..00000000 --- a/framework/sdk/frameworks/h3.mdx +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: "Integrate Novu with H3" -sidebarTitle: "H3" ---- - -```tsx -import { createApp, eventHandler, toNodeListener } from "h3"; -import { serve } from "@novu/framework/h3"; -import { createServer } from "node:http"; -import { myWorkflow } from "./novu/workflows"; - -const app = createApp(); -app.use( - "/api/novu", - eventHandler( - serve({ - workflows: [myWorkflow], - }) - ) -); - -createServer(toNodeListener(app)).listen(process.env.PORT || 3000); -``` diff --git a/framework/sdk/frameworks/nextjs.mdx b/framework/sdk/frameworks/nextjs.mdx deleted file mode 100644 index be46b38b..00000000 --- a/framework/sdk/frameworks/nextjs.mdx +++ /dev/null @@ -1,29 +0,0 @@ ---- -title: "Integrate Novu with Next.js" -sidebarTitle: "Next.js" ---- - -## App Router - - -```ts app/api/novu/route.ts -import { serve } from "@novu/framework/next"; -import { myWorkflow } from "../../novu/workflows"; - -export const { GET, POST, PUT, OPTIONS } = serve({ workflows: [myWorkflow] }); -``` - - - -## Pages Router - - - ```ts pages/api/novu.ts - import { serve } from "@novu/framework/next"; - import { myWorkflow } from '../../novu/workflows'; - - export default serve({ workflows: [myWorkflow] }); - ``` - - -Check out a comprehensive [guide on sending notifications in Next.js with React email](/guides/framework-guides/framework-react-email). diff --git a/framework/sdk/frameworks/nuxt.mdx b/framework/sdk/frameworks/nuxt.mdx deleted file mode 100644 index 80f0ca90..00000000 --- a/framework/sdk/frameworks/nuxt.mdx +++ /dev/null @@ -1,19 +0,0 @@ ---- -title: "Integrate Novu with Nuxt" -sidebarTitle: "Nuxt" ---- - -Novu Framework works out of the box with Nuxt.js. You can use the `@novu/framework/nuxt` package to serve your Bridge endpoint in your Nuxt.js application. - -For a boilerplate project with Vue Email, view: [Nuxt.js Boilerplate](https://github.com/novuhq/novu-framework-nuxt-example/) - - -```ts API Routes -import { serve } from "@novu/framework/nuxt"; -import { myWorkflow } from "../workflows"; - -export default defineEventHandler(serve({ workflows: [myWorkflow] })); -``` - - -Check out a comprehensive [guide on sending notifications in Nuxt with Vuemail](/guides/framework-guides/framework-nuxt-vuemail). diff --git a/framework/sdk/frameworks/remix.mdx b/framework/sdk/frameworks/remix.mdx deleted file mode 100644 index be68e636..00000000 --- a/framework/sdk/frameworks/remix.mdx +++ /dev/null @@ -1,18 +0,0 @@ ---- -title: "Integrate Novu with Remix" -sidebarTitle: "Remix" ---- - -```tsx -import { serve } from "@novu/framework/remix"; -import { myWorkflow } from "../novu/workflows"; - -const handler = serve({ - workflows: [myWorkflow], - }); - -export { handler as action, handler as loader }; -``` - -Check out a comprehensive [guide on sending notifications in Remix with React email](/guides/framework-guides/framework-remix). - diff --git a/framework/sdk/frameworks/sveltekit.mdx b/framework/sdk/frameworks/sveltekit.mdx deleted file mode 100644 index b3bfcebc..00000000 --- a/framework/sdk/frameworks/sveltekit.mdx +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: "Integrate Novu with SvelteKit" -sidebarTitle: "SvelteKit" ---- - -```tsx -import { myWorkflow } from '../workflows'; -import { serve } from '@novu/framework/sveltekit'; - -export const { GET, POST, PUT } = serve({ workflows: [myWorkflow] }); -``` - -Check out a comprehensive [guide on sending notifications in Svelte with Svelte-email](/guides/framework-guides/framework-svelte). - diff --git a/framework/sdk/typescript.mdx b/framework/sdk/typescript.mdx deleted file mode 100644 index 57553809..00000000 --- a/framework/sdk/typescript.mdx +++ /dev/null @@ -1,36 +0,0 @@ ---- -title: "Typescript" ---- - -import { EchoTerminal } from '/snippets/echo-terminal.mdx'; - -Novu Framework was built and optimized for extreme focus on Developer Experience. -Our `@novu/framework` SDK is written in Typescript, and we recommend using Typescript for your own projects as well. - - - -## Type Safe Workflow Payloads - -When defining a [workflow payload](/framework/concepts/payload) schema, our SDK will automatically infer it to a Typescript interface. - -```typescript -import { workflow } from '@novu/framework'; - -const myWorkflow = workflow('new-signup', async ({ step, payload }) => { - await step.email('send-email', () => { - return { - subject: 'Hello World', - // The payload object here is type-safe - body: `Hi ${payload.name}, welcome to our platform!` - } - }); -}, { - // JSON Schema for validation and type-safety. Zod, and others coming soon. - // https://json-schema.org/draft-07/json-schema-release-notes - payloadSchema: { properties: { name: { type: 'string' }}}, -}); -``` - -## Type Safe Steps - -Similarly, when defining a [step](/framework/concepts/steps) schema, our SDK will automatically infer it to a Typescript interface. diff --git a/framework/steps/chat.mdx b/framework/steps/chat.mdx deleted file mode 100644 index a2cd3980..00000000 --- a/framework/steps/chat.mdx +++ /dev/null @@ -1,50 +0,0 @@ ---- -title: "Chat Channel Step" -sidebarTitle: "Chat" ---- - -Use the chat channel step to send a message for providers like: Slack, WhatsApp, Discord and others. - -```tsx -await step.chat('send-chat', async () => { - return { - body: 'A new post has been created', - }; -}); -``` - -## Provider Overrides - -Some providers offer a rich DSL for styling the messages. Novu Framework offers a robust API to leverage the provider specific capabilities using the step `providers` option object. - -Let's see an example of how to send a message to Slack using the `providers` option objet. - -```tsx -await step.chat('send-chat', async () => { - return { - body: 'A new post has been created', - }; -}, { - providers: { - slack: ({ inputs }) => ({ - text: 'A new post has been created', - blocks: [ - { - type: 'section', - text: { - type: 'mrkdwn', - text: 'A new post has been created', - }, - }, - ], - }), - } -}); -``` - -Default chat `body` field is still specified as a fallback value in case a provider specific configuration is not used. - - - - Currently we only support `Slack` provider overrides. We are working on adding more providers. - diff --git a/framework/steps/custom.mdx b/framework/steps/custom.mdx deleted file mode 100644 index 0ec8b724..00000000 --- a/framework/steps/custom.mdx +++ /dev/null @@ -1,84 +0,0 @@ ---- -title: "Custom Action Step" -sidebarTitle: "Custom" -description: "Used to execute any custom code as a step in the workflow." ---- - -A custom steps allows to execute any custom logic and persist in the durable execution context. The result of this step can be used in subsequent steps. - -## Common usecases - -- Making an API call to 3rd party service -- Fetch data from a database to be used in subsequent steps -- Execute a custom logic to transform data -- Custom provider implementation - -## Custom Step Interface - -```tsx -const stepResult = await step.custom('custom-step', async () => { - return { - item_name: 'A product name', - item_price: 100, - }; -}, { - outputSchema: { - type: 'object', - properties: { - item_name: { type: 'string' }, - item_price: { type: 'number' }, - }, - required: ['item_name', 'item_price'], - } -}); -``` - -### Output Schema Definition - -This JSON Schema definition is used to validate the output of the custom step. If the output does not match the schema, the workflow will fail. -Novu Framework will infer the Typescript interface from the JSON Schema definition. - -### Return Value - -The Custom Step function should return a valid serializable object. The return value will be persisted in the durable execution context. - -## Using the Custom Step Result - -The result can only be used in the `resolver` of the step/providers/skip functions of subsequent steps. - -```typescript -workflow('hello-world-workflow', async ({ payload }) => { - const task = await step.custom('fetch-db-data', async () => { - const taskData = db.fetchTask(payload.task_id); - return { - task_id: taskData.id, - task_title: taskData.title, - complete: taskData.complete, - }; - }, { - outputSchema: { - type: 'object', - properties: { - task_title: { type: 'string' }, - task_id: { type: 'string' }, - complete: { type: 'boolean' }, - }, - required: ['task_id', 'complete'], - } - }); - - await step.email('send-email', () => { - return { - subject: `Task reminder for ${task.task_title}`, - body: 'Task is not yet complete. Please complete the task.' - } - }, { - // Only send the reminder E-mail if the task is not complete - skip: () => !task.complete - }) -}); - - - -``` - diff --git a/framework/steps/delay.mdx b/framework/steps/delay.mdx deleted file mode 100644 index 83b17250..00000000 --- a/framework/steps/delay.mdx +++ /dev/null @@ -1,16 +0,0 @@ ---- -title: "Delay Action Step" -sidebarTitle: "Delay" ---- - -Use the delay action whenever you need to pause the execution of your workflow for a period of time. - -```tsx -await step.delay('delay-1-week', async () => { - return { - unit: 'weeks', // 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'months' - amount: 1, // the number of units to delay workflow execution for - }; -}); -``` - diff --git a/framework/steps/digest.mdx b/framework/steps/digest.mdx deleted file mode 100644 index be913513..00000000 --- a/framework/steps/digest.mdx +++ /dev/null @@ -1,45 +0,0 @@ ---- -title: "Digest Action Step" -sidebarTitle: "Digest" ---- - -The digest engine is designed to streamline notifications by accumulating multiple trigger events into one coherent message before delivery to the subscriber. - -This function proves beneficial when a user needs to be alerted about numerous triggers, but it's important to minimize the frequency of notifications. Novu automates this process, batching incoming trigger events according to a unique subscriberId. There is also the option to include a digestKey, providing additional control over event digestion workflows. - -## Step Interface - -```tsx -const digestResult = await step.digest('digest-3-days', async () => { - return { - unit: 'days', // 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'months' - amount: 3, // the number of units to digest events for - }; -}); -``` -## Using the digested triggers - -The digest function returns an array of triggers that have been digested. -You can use this array to perform any necessary actions on the digested triggers. Like Sending and email, or updating a database. - -```tsx -const digestResult = await step.digest('digest-3-days', async () => { - return { - unit: 'days', // 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'months' - amount: 3, // the number of units to digest events for - }; -}); - -await step.email('send-email', async () => { - return { - subject: 'Digest Email', - body: `You have ${digestResult.events.length} new events`, - }; -}); -``` - -The digest step returns an object with `events` array. Each event in the array has the following properties: - -- `id` - The `job id` of the digested event -- `time` - The time when the event was triggered -- `payload` - The original payload passed to the event diff --git a/framework/steps/email.mdx b/framework/steps/email.mdx deleted file mode 100644 index 9a990ef9..00000000 --- a/framework/steps/email.mdx +++ /dev/null @@ -1,20 +0,0 @@ ---- -title: "Email Channel Step" -sidebarTitle: "Email" ---- - -E-mail expected to be an object with the following properties: - -- `subject` - The subject of the email -- `body` - The HTML body of the email - -Any content generation framework like [React Email](/framework/integrations/react-email) can be used here to generate the HTML Content. - -```tsx -await step.email('send-email', async (inputs) => { - return { - subject: 'You received a post', - body: `A new post has been created`, - }; -}); -``` diff --git a/framework/steps/in-app.mdx b/framework/steps/in-app.mdx deleted file mode 100644 index 673be38b..00000000 --- a/framework/steps/in-app.mdx +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: "In-App Channel Step" -sidebarTitle: "In-App" ---- - -Novu offers notification center component that can be easily embedded into your application. Read more about it [here](/notification-center/introduction). - -```tsx -await step.inApp('send-in-app', async (inputs) => { - return { - body: `A new post has been created`, - }; -}); -``` diff --git a/framework/steps/introduction.mdx b/framework/steps/introduction.mdx deleted file mode 100644 index 1f39cb80..00000000 --- a/framework/steps/introduction.mdx +++ /dev/null @@ -1,90 +0,0 @@ ---- -title: "Workflow Steps" -sidebarTitle: "Introduction" -description: "Steps control the execution flow of your workflow, whilst Novu provides the context of your notification delivery events back into the Workflow execution context." ---- - -## Supported Steps - -Novu Supports Channel and Action steps to be used as part of the workflow execution. - -### Channel Steps -- [E-mail](/framework/steps/email) -- [Chat](/framework/steps/chat) -- [SMS](/framework/steps/sms) -- [Push](/framework/steps/push) -- [In-App](/framework/steps/in-app) - -### Action steps -- [Delay](/framework/steps/delay) -- [Digest](/framework/steps/digest) - -## Channel Steps Interface - -Channel steps are used to send notifications to your Subscribers. Each channel can optionally configure the provider to customize content, enabling you to create enhanced content aligned with the specification of the provider. For example, you might like to use Slack blocks to send your Chat message. - -All channels follow the same interface: - -```tsx -await step.email('new-post', async (inputs) => { - return { - subject: 'You received a post', - body: `A new post has been created`, - } -}, options); -``` - -- `new-post` is the unique to the workflow identifier for this step. You cannot have 2 steps using the same identifier -- `resolver` function, used to return the content of the channel. -- `options` this is an optional configuration object that defines: [Input Schema](/framework/concepts/inputs), [Provider Overrides](#provider-overrides), skip and other configurations... - -## Provider Overrides - -Provider overrides are used to customize the content of the notification for a specific provider. For example, you might like to use Slack blocks to send your Chat message. - -Those overrides are mapped directly to the provider specific SDKs, so you can use the same properties and methods that you would use in the provider SDK. - -```tsx -await step.chat('send-chat', async () => { - return { - body: 'A new post has been created', - }; -}, { - providers: { - slack: ({ inputs }) => ({ - text: 'A new post has been created', - blocks: [ - { - type: 'section', - text: { - type: 'mrkdwn', - text: 'A new post has been created', - }, - }, - ], - }), - } -}); -``` - - - Currently we only support `Slack` provider overrides. We are working on adding more providers. - - -When overriding a provider specific behavior, Novu still requires the default content to be returned from the resolver function. -This is because the default content is used for the fallback provider, or when the provider override is not available. - -## Skip Step - -A step can be conditionally skipped by returning `true` from the resolver function. This is helpful when you want to use your existing user-preference system instead of Novu. -Or conditionally skip a step if it's not relevant any more for delivery. - -```tsx -await step.chat('send-chat', async () => { - return { - body: 'A new post has been created', - }; -}, { - skip: () => true, -}); -``` diff --git a/framework/steps/push.mdx b/framework/steps/push.mdx deleted file mode 100644 index 546718b8..00000000 --- a/framework/steps/push.mdx +++ /dev/null @@ -1,13 +0,0 @@ ---- -title: "Push Channel Step" -sidebarTitle: "Push" ---- - -```tsx -await step.push('send-push', async () => { - return { - subject: 'You received a post', - body: 'A new post has been created', - }; -}); -``` diff --git a/framework/steps/sms.mdx b/framework/steps/sms.mdx deleted file mode 100644 index f8c72e8d..00000000 --- a/framework/steps/sms.mdx +++ /dev/null @@ -1,13 +0,0 @@ ---- -title: "SMS Channel Step" -sidebarTitle: "SMS" ---- - - -```tsx -await step.sms('send-sms', async () => { - return { - body: 'A new post has been created', - }; -}); -``` diff --git a/getting-started/how-novu-works.mdx b/getting-started/how-novu-works.mdx index 2fbd55b0..c4a89ca4 100644 --- a/getting-started/how-novu-works.mdx +++ b/getting-started/how-novu-works.mdx @@ -1,21 +1,12 @@ --- -title: 'How Does Novu Work?' -description: '' -icon: 'map' +title: "How Does Novu Work" --- -Novu comprises two fundamental components: an API for data exchange and a dashboard for designing notifications and their accompanying logical rules. +The [Novu Framework](/sdks/framework/typescript/overview) enables you to work in your application, use your own libraries, and connect to your production environment during workflow execution over HTTPS. -This section provides a broad overview of Novu's functionality, delving into key concepts for your initial Novu journey. If you prefer to dive into development, explore our **[Quick Start guides](/quickstarts/overview)**. +The framework exports the client you use to define and serve your workflows and their steps over HTTPS. +The workflows can be added to your preferred framework. During workflow execution Novu Cloud makes HTTPS requests to your [Bridge Endpoint](/concepts/endpoint) for _just-in-time_ content and [Subscribers](/concepts/subscribers) resolution. -## Architecture - -Novu's Object Communication Layer (OCL) is a structured framework that separates communication tasks into specialized components. -Similar to microservices, these components handle specific roles like message passing and data management. By doing so, OCL enhances system maintenance and scalability, making it easier to integrate new functionalities. - -Novu's approach goes beyond this, optimizing communication protocols for seamless collaboration and adaptability. It's like providing a playbook for coordinating a dynamic game, ensuring all players are synchronized for any challenge. - - + + + diff --git a/getting-started/introduction.mdx b/getting-started/introduction.mdx index 97a046ca..82d01bba 100644 --- a/getting-started/introduction.mdx +++ b/getting-started/introduction.mdx @@ -1,186 +1,40 @@ --- -title: "What is Novu?" -description: "Novu is a full-stack (UI, API & GitOps) open source notification infrastructure tool and platform for building, managing, delivering, and monitoring all types of end-user notifications." -icon: "arrow-right-to-arc" +title: "What is Novu" +description: "Novu is a full-stack (UI Components, API, and Framework) open source notification infrastructure platform for building, managing, delivering, and monitoring all types of end-user notifications." --- -Novu supports a variety of common notification channels out-of-the-box, including **email**, **SMS**, **push**, **in-app**, and **chat**. Our platform is a developer-first product built for engineers that need to simplify the complexities of notification management, while working closely with product and marketing teams that need to provide, edit, and maintain notification content. - - - - - - Get started in minutes - - - - Embed a real-time notification center component in your React application. - - - Embed a real-time notification center component in your Vue application. - - - Embed a real-time notification center component in your Angular application. - - - - - - - - - - - } color="#16a34a" href="/quickstarts/kotlin"> - Embed a real-time notification center component in your Kotlin application. - - - } color="#16a34a" href="/quickstarts/php"> - Embed a real-time notification center component in your PHP application. - - - - } - color="#16a34a" - href="/quickstarts/ruby"> - Embed a real-time notification center component in your Ruby application. - - - +import { EchoTerminal } from "/snippets/echo-terminal.mdx"; - - - - You can interface with Novu's API either over REST, or via libraries for certain languages. Below is a list of our official, supported libraries. - ### Client Side - The Client side libraries and components helps to add a fully functional notification center to your application. - - - Embed a real-time notification center component in your React application. - - - Embed a real-time notification center component in your Vue application. - - - Embed a real-time notification center component in your Angular application. - - - Embed a real-time notification center component in your JavaScript application. - - - Embed a real-time notification center inside an iframe using our embedded script. - - - Embed a notification system into any framework or vanilla JavaScript project, without being constrained by our default UI or dependencies. - - + Novu is a developer-first product built for engineers looking to deliver a notifications platform for products. Novu simplifies the complexities of notification management for developers who can then empower the product and marketing teams that need to edit and maintain notification content and copy. Novu supports a variety of common notification channels out-of-the-box, including **Email**, **SMS**, **Push**, **Inbox**, and **Chat**. -### Server Side - -The Server Side libraries help to trigger notifications and interact with all of Novu’s API seamlessly. - - - - } color="#ea5a0c" href="/sdks/nodejs"> - Connect your Node app to Novu via the Node.js SDK. - - - }color="#0285c7" href="/sdks/go"> - Connect your Golang app to Novu via the Go SDK. - - - } color="#16a34a" href="/sdks/php"> - Connect your PHP app to Novu via the PHP SDK. - - - Connect your Laravel app to Novu via the Laravel SDK. - - - } color="#dc2626" href="/sdks/dotnet"> - Connect your C#/.NET app to Novu via the .NET SDK. - - - - } - color="#dc2626" - href="/sdks/ruby" - -> - - Connect your Ruby app to Novu via the Ruby SDK. - - - - } color="#dc2626" href="/sdks/java"> - Connect your Java app to Novu via the Java SDK. - - - - - - - - - - - - } - color="#16a34a" - href="/sdks/kotlin" - > - Connect your Kotlin app to Novu via the Kotlin SDK. - - - }color="#dc2626" href="/sdks/python"> - Connect your Python app to Novu via the Python SDK. - - +The [Novu Cloud Platform](https://dashboard.novu.co) provides an intuitive internal user interface, embeddable UI end-user components, and a code-first workflow capability. + + - - +## Quick start -The [Novu Cloud SaaS](https://web.novu.co) platform provides an intuitive internal user interface, embeddable UI end-user components, and a hybrid code-first workflow capability. +Start building with Novu by following our Quick Start guides. These guides provide step-by-step instructions for integrating Novu into your application. -Novu eliminates the significant burden of building, hosting, and managing a notifications infrastructure that all modern software businesses need in order to notify and engage staff, end users, and customers. + + + + + + + + + + + + + + + + ## Notification types @@ -199,77 +53,53 @@ Novu supports different notification types, including user-generated and machine - Typically transactional, such as order updates, password resets, account verifications, or login/OTP codes. - Can also be used to provide product updates, such as new features or bug fixes. -### Promotional notifications - -- Centered on promoting products, services, or events. -- Can be used to generate leads, increase sales, or drive engagement. - -### Key Differences +### Key differences - Product notifications are triggered by user activity, while promotional notifications are sent at the discretion of the marketer. - Product notifications are typically more transactional in nature, while promotional notifications are more marketing-oriented. - Product notifications are often sent to all subscribers, while promotional notifications can be targeted to specific audiences. -## Notification channels -Novu supports all major notification channels, and integrates with multiple delivery providers for each one. Each channel can use the same source notification content, as desired. -- [Email](/channels-and-providers/email/overview) -- [Push](/channels-and-providers/push/overview) -- [SMS](/channels-and-providers/sms/overview) -- [Chat](/channels-and-providers/chat/overview) -- [In-App](/channels-and-providers/in-app/introduction) - ## Who is Novu for? -How you use and get started with Novu depends on your role. While it’s implemented initially by engineering and development teams, Novu unifies everyone in an organization that authors, creates, sends, manages and measures results from notifications being sent to end users. Novu empowers engineers to deliver notification platforms for product teams. +How you use and get started with Novu depends on your role. While it’s initially implemented by engineering and development teams, Novu unifies everyone in an organization that authors, creates, sends, manages, and measures results from notifications being sent to end users. Novu empowers engineers to deliver notification platforms for product teams. -### Novu for developers and engineering teams +### Novu for engineers -Novu empowers developers and engineering teams to quickly deliver a fully extensible notifications platform for product teams to create captivating notification experiences. +Novu empowers developers and engineering teams to quickly deliver a fully extensible notifications platform for product teams to create captivating notification experiences. -We provide the following proven stack for developers to simply integrate into their products: +We provide the following proven stack for developers to simply integrate notifications into their products: -- **[Unified API](/api-reference/overview) & [SDK clients](/sdks/introduction)** for managing all channels and platforms across multiple programming languages. -- **[Prebuilt, customisable UI components](/notification-center/introduction#ui-libraries)** for in-app user notification feeds and preference experiences. -- **[Integration with multiple delivery providers](/channels-and-providers/introduction)**, allowing you to continue using your preferred vendors with Novu. -- **[Scalable, reliable Novu Cloud SaaS infrastructure](https://web.novu.co)** developed from scratch to meet the demands of high-volume notification delivery and storage (think hundreds of millions of notifications). -- **[Observability](/activity-feed/introduction)** for delving into the lifecycle of notification’s success or failure. Eliminate guesswork of how, when, and why a user receives a notification. +- **[Code-first Notification Framework](/sdks/framework/typescript/overview)** Opinionated, yet flexible, Framework for building and managing notification workflows. +- **[JSON Schema Based](/api-reference/overview)** Controls to craft a no-code visual editor to enable non-technical team members to modify content and behaviour. +- **[Prebuilt, customisable UI components](/inbox/introduction)** for in-app user notification feeds and preference experiences. +- **[Integration with multiple delivery providers](/integrations/providers/introduction)**, allowing you to continue using your preferred vendors with Novu. +- **[Scalable, reliable Novu Cloud SaaS infrastructure](https://dashboard.novu.co)** developed from scratch to meet the demands of high-volume notification delivery and storage (think hundreds of millions of notifications). +- **Observability** for delving into the lifecycle of a notification's success or failure. Eliminate guesswork of how, when, and why a user receives a notification. - **Comprehensive documentation**, implementation guides, recipes and illustrative examples. - **Compliance and security** for safely managing your data. -- **Open source** based provides trust, generates significant community input, and enables you to deploy and self-host a Novu Project instance into any environment of your choosing. - -Notification content can be written in a variety of common content tooling, including [React](/framework/integrations/react-email), [Vue-email](/framework/integrations/vue-email), [MJML](/guides/framework-guides/framework-mjml), and more. Content can also be customized and hydrated using any datasource. +- **Open source** provides transparency you can trust, cultivates community contributions for fast improvement, and enables you to deploy and self-host a Novu instance into any environment of your choosing. +Notification content can be written in a variety of common content tooling, including [React](/integrations/react-email), [Vue-email](/integrations/vue-email), MJML, and more. Content can also be customized and hydrated using any datasource. ### Novu for product teams -We are well aware of the friction that often exists between engineering and product teams, and have built features that allow product teams to customize notification experiences easily and safely—without risking disrupting or breaking important integrations and logic. +We are well aware of the friction that often exists between engineering and product teams, and have built features that allow product teams to customize notification experiences easily and safely—without the risk of breaking important integrations and logic. -Product teams can craft beautiful notification content and campaigns in any content framework of their choice. +Product teams can craft beautiful notification content and campaigns in any content framework of their choice. -- They can modify and manage notification UIs that engineers have built via Novu workflows. -- They gain valuable insights into user engagement with notifications via logs and analytics. -- They can use their preferred notification content editors. -- They can focus on crafting impactful notification content while engineers +{/* Since we're speaking to both engineering and product in this section of the documentation, we should address product teams directly instead of using third-person "them" */} +- Modify and manage notification UIs that engineers have built via the Novu Framework. +- Gain valuable insights into user engagement with notifications via logs and analytics. +- Use your preferred notification content editors. +- Craft impactful notification messaging, verbiage, and cadences. +- Run experiments to improve user experience without requiring engineering effort. ### Novu for end users -When implemented, most end users will not be aware that it’s Novu behind the scenes, other than that they now receive notifications where and when they expect them. - -The [Novu Notification Center](/notification-center/introduction) is an embeddable component that when included in your app, enables users to view and set their own notification preferences, including channel, preferred language, and more. - -Novu ensures fast delivery and beautiful notification experiences for end users regardless of the channels involved. - -With Novu, your app users receive instant notifications via **email**, **in-app alerts**, **SMS**, **push notifications**, and **chat**. +Once implemented, most end users will not be aware that it’s Novu behind the scenes, other than that they now receive notifications where and when they expect them. -## Use Cases +The [Inbox Component](/inbox/introduction) is an embeddable component that, when included in your app, enables users to view and set their own notification preferences--such as channel, language, timezone, and more. -Novu is heavily customizable, and can tackle nearly any notification requirement, no matter how simple or complex. These are some of the more common use cases: +Novu ensures fast delivery and beautiful notification experiences for end users regardless of the channels involved. -- **Manage multiple notification providers:** Novu empowers you to manage all your notification delivery providers across email, SMS, push and chat in one place. Developers can add new notification providers across different channels without changing the app codebase. -- **Centralize legacy notification systems:** Reduce notification complexity, unify management and visibility into one platform. -- **Centralized debugging of notifications across multiple channels**: Novu allows you understand and debug notifications sent across different channels in one place. -- **In-app Notification Center:** Novu provides a real-time embedded notification center widget that can simply be dropped into any web application to enable end users to manage their own notification preferences. -- **Notification personalization:** Novu provides a user preferences feature that gives your end users a say in how they want to receive notifications. -- **Notification content management:** Novu provides the ability to integrate any content framework and tool of your choice to manage notification content across teams. -- **Send notifications in users language of choice**: Novu allows you to send notifications in preferred users language. App and notification internalization increases users engagement and retention in your product. -- **Reduce notification alert fatigue**: Novu prevents notification fatigue by providing the ability to batch multiple notifications and send as one to subscribers (end users). Leverage Novu in digesting multiple notification events. +With Novu, your app users receive instant notifications via **email**, **Inbox alerts**, **SMS**, **push notifications**, and **chat**. diff --git a/getting-started/non-technical.mdx b/getting-started/non-technical.mdx new file mode 100644 index 00000000..6d36cecb --- /dev/null +++ b/getting-started/non-technical.mdx @@ -0,0 +1,41 @@ +--- +title: "No-Code tools to manage your notifications" +sidebarTitle: "For Non-Developers" +description: "Change notification messaging, verbiage, and cadence without requiring engineering's help by using the Novu Dashboard UI." +--- + +Novu Framework was designed to bridge the gap between developers and non-developers in the team. + +- **Developers** - Define and abstract away complex logic, data manipulation, hydration, html, and etc... +- **Non-Developers** - Use the Novu Dashboard UI to control the content and behavior of the notifications using [Controls](/concepts/controls). + +## How to modify Controls? + +Controls are modified directly in the [Novu Dashboard UI](https://dashboard.novu.co), under the 'Controls' tab of the Workflow editor page. +Upon saving the changes, the new Control values will be used in the notification sent to your subscribers. + +## Dynamic payload data + +Dynamic data passed as part of the trigger payload can be easily used inside of the Controls. For example, you can pass a `user_name` as part of the payload and use it in the controls as `{{user_name}}` to personalize the notification. + +## Common usecases + +### Change notification content + +A control can be as broad as `content` or as specific as `2fa_code_title_color`. This allows you to change the content of the notification without needing to touch the code. + +### Change Digest or Delay frequency + +Controls can be used for controlling any aspect of the step configuration, for example: + +- Delay amount +- Digest length +- Digest type (Daily, Weekly, Monthly) +- etc... + +Read more about [digest strategies and how to prevent notification fatigue.](https://novu.co/blog/digest-notifications-best-practices-example/) + +### Control structure and layout + +Controls can be used to show/hide or rearrange the layout of the notification. +For example, you can have an input to show/hide a button, or change the position of a specific email section. diff --git a/getting-started/novu-sign-up.mdx b/getting-started/novu-sign-up.mdx deleted file mode 100644 index cc4f24c9..00000000 --- a/getting-started/novu-sign-up.mdx +++ /dev/null @@ -1,36 +0,0 @@ ---- -title: 'Sign Up on Novu Cloud' -description: 'Sign up to our managed service' -icon: 'user-plus' ---- - -## Introduction - -Novu’s aim is to simplify sending notifications across platforms. As such, we provide simple components, APIs, SDKs and more to help you manage product communication across channels. - -This comprehensive guide will help you kickstart your journey with Novu and get your hands dirty! - -Everything you need to get up and running successfully can be found here. Let’s begin! - -## Signing up to Novu Cloud - -Novu Cloud is our managed service that lets you oversee all aspects of your product communication from a user-friendly web dashboard. With Novu Cloud, you can handle various communication channels, create and manage workflows, switch organizations and monitor your activity, ensuring that you get comprehensive control over your product communication and that your message reaches your customers effectively. - -To be able to do this and a lot more, you need to sign up for Novu Cloud: - -1. Head to [Novu Cloud](https://web.novu.co/auth/signup?utm_campaign=docs-gs-signup). You’ll see this: - -2. To sign up, you can either use your email and password or sign up using Github. Choose what you wish to proceed with, read the terms and conditions, check its checkbox and click the sign-up button. - -3. You’ll then be asked to create an Organization. Organizations help you segregate communication between your different products. You can learn more about organisations here. Simply enter your organization name and click on the ‘create’ button: - -4. On creating an organization, you’ll be greeted with the get started page: - -This gives you access to a lot of options: - -1. Accessing support -2. Managing your account -3. Configuring different channels -4. Navigating to any other sections of the dashboard - -From here, you can navigate to any part of the web dashboard. Let’s now see Novu in action! \ No newline at end of file diff --git a/getting-started/send-your-first-notification.mdx b/getting-started/send-your-first-notification.mdx deleted file mode 100644 index 6d4f2d7a..00000000 --- a/getting-started/send-your-first-notification.mdx +++ /dev/null @@ -1,299 +0,0 @@ ---- -title: 'Send Your First Notification' -sidebarTitle: 'Send First Notification' -description: 'Learn how to send your first notification via the Novu Cloud dashboard.' -icon: 'bell' ---- - -Sending the first notification is fairly simple, and can be done from within the web dashboard. To send your first notification, follow these steps: - -## Workflow for Email notifications - -1. Navigate to the workflow section from the left sidebar: - - - - - -2. Click on the `Blank Workflow` button to create an empty workflow: - - - - - -3. You are now in the workflow editor. Here you can add steps to your workflow. We’ll just drag the email node from the right sidebar to our workflow as illustrated below: - - - - - -4. Now, simply click on the email node, and add the subject and the notification - text in the email node editor: - - - - - -5. Now, update the workflow and save it as shown in the image below: - - - - - -6. Now, to execute this workflow, click on the `get snippet` button from the workflow editor: - - - - - -7. From the `run a test` tab, you can change the email to which you want the notification. Once you’ve done that, simply click on the `Run Trigger` button on the bottom right: - - - - - -8. Now, go to the inbox and you’ll see the email notification there. Don’t forget also to check spam as it may sometimes land in there: - - - - - -9. This gif below captures the entire process: - - - - - -## Workflow for In-App notifications - -Creating workflows for sending in-app notifications is pretty straightforward. Just follow the following steps and you'll be good to go: - -1. Head to `workflows` on the [Novu web dashboard](https://web.novu.co/workflows?utm_campaign=docs-gs-sendfirstnotification). - - - - - -2. Click on the Blank Workflow button to create a workflow from scratch. - - - - - - - If you want to avoid creating a workflow from scratch, then instead of - choosing `Blank Workflow`, select `All Templates` and you'll find a bunch of - workflow templates that you can customize and use: - - - -
    - - - -
    - -3. You'll now end up in the workflow editor. Workflow editor, as the name suggests, is the place from where you can edit workflows. You can select which [channels](/channels-and-providers/introduction#channels) you want to send notifications in, add [digest](/guides/add-digest-to-inapp-notifications) to your in-app notifications and more. - - - - - -4. For your use case here, we'll simply add an `in-app` node to our workflow. - - - Don't forget to rename your workflow to an appropriate name. - - - - - -5. Now, the `in-app` node has a lot of options for you to configure: - - - - - -- **Preview**: This shows you a glimpse of what each notification item will look like in the Notification Center UI. -- **Avatar:** If turned on, each notification item will show the avatar of the subscriber. -- **Action:** With this, you can add a primary and secondary call to action button to each notification item. -- **Notification Feeds:** This displays a stream of specific notifications. You can have multiple feeds to show specific notifications in multiple tabs. -- **Redirect URL** - This is the URL to which a subscriber can be directed when they click on a notification item. -- **Filter** - This feature allows you to configure the criteria for delivering notifications. For instance, you can apply a filter based on a subscriber's online status to send them an email if they were online within the last hour. - -6. In this editor, you need to plug in the variable that your backend is expecting in the payload: - - - - - -7. You can even use [handlebars](/content-creation-design/handlebars-helpers) to further customise this. - -8. Once you're done configuring it, simply click the `update` button on the top right corner. - - - - - -9. Now, simply click on the `Get Snippet` button to get the trigger code snippet. - - - - - -10. You'll get the code that you need to plug into your backend to be executed whenever an event fires. - - - - -
    - - Don't forget to use your actual `API_KEY` and `subscriberId`. - - -That's it, you've successfully built a workflow for sending in-app notifications! - -## Workflow for SMS notifications - -SMS workflows are not very different from [email](/getting-started/send-your-first-notification#workflow-for-email-notifications) and [in-app](localhost:3001/getting-started/send-your-first-notification#workflow-for-in-app-notifications) workflows we've created above. Let's take a look at it: - -1. Head to `Workflows` on the [Novu web dashboard.](https://web.novu.co/workflows?utm_campaign=docs-gs-sendfirstnotification) - - - - - -2. Click on the `Blank Workflow` button to create a workflow from scratch. - - - - - - - If you want to avoid creating a workflow from scratch, then instead of - choosing `Blank Workflow`, select `All Templates` and you'll find a bunch of - workflow templates that you can customize and use: - - - -
    - - - -
    - -3. You'll now end up in the workflow editor. Workflow editor, as the name suggests, is the place from where you can edit workflows. You can select which [channels](/channels-and-providers/introduction#channels) you want to send notifications in, add [digest](/guides/add-digest-to-inapp-notifications) to your in-app notifications and more. - - - - - -4. Since we're creating a workflow for SMS notifications, we'll drag the `SMS` step to our workflow, as shown in this picture below: - - - - - -5. If you hover over the SMS node, you will get a message saying 'Required - SMS content`. Click on the SMS node and add a message to it. This is what will be sent as a notification in the SMS. - - - - -
    - - Instead of having static messages, you can have your notifications be - dynamic by using `handlebars`. Take a look - [here](/content-creation-design/handlebars-helpers) for more. If your - backend is expecting data in the payload, plug that variable here in double - braces. - - -6. Once you’re done configuring it, simply click the update button on the top right corner. - - - - - -7. Now, simply click on the Get Snippet button to get the trigger code snippet. - - - - - -8. You’ll get the code that you need to plug into your backend to be executed whenever an event fires. - - - - - Don’t forget to use your actual API_KEY and subscriberId. - That’s it, you’ve successfully built a workflow for sending in-app - notifications! - -## Workflow for Chat notifications - -Chat workflows are quite similar to [email](/getting-started/send-your-first-notification#workflow-for-email-notifications), [in-app](localhost:3001/getting-started/send-your-first-notification#workflow-for-in-app-notifications), and [SMS workflows](/getting-started/send-your-first-notification#workflow-for-sms-notifications) workflows we've created above. Let's take a look at it in detail: - -1. Head to `Workflows` on the [Novu web dashboard.](https://web.novu.co/workflows?utm_campaign=docs-gs-sendfirstnotification) - - - - - -2. Click on the `Blank Workflow` button to create a workflow from scratch. - - - - -
    - - If you want to avoid creating a workflow from scratch, then instead of - choosing `Blank Workflow`, select `All Templates` and you'll find a bunch - of workflow templates that you can customize and use: - - - -
    - - - -
    - -3. You'll now end up in the workflow editor. Workflow editor, as the name suggests, is the place from where you can edit workflows. You can select which [channels](/channels-and-providers/introduction#channels) you want to send notifications in, add [digest](/guides/add-digest-to-inapp-notifications) to your in-app notifications and more. - - - - - -4. Just add this `Chat` node to the workflow. - - - - - -5. In the `Chat` node, you can enter the notification message. This is what will get sent to the [subscriber](/subscribers/subscribers) as a notification. If you're sending dynamic messages, use the variable that is expected in your payload in double braces here. Using [handlebars](/content-creation-design/handlebars-helpers), you can further extend this functionality. - - - - - -6. Once you’re done configuring it, simply click the update button on the top right corner. - - - - - -7. Now, simply click on the Get Snippet button to get the trigger code snippet. - - - - - -8. You’ll get the code that you need to plug into your backend to be executed whenever an event fires. - - - - - Don’t forget to use your actual API_KEY and subscriberId. - That’s it, you’ve successfully built a workflow for sending chat notifications! - -Don't forget to check out our [guides](/guides/add-digest-to-inapp-notifications) and [demo apps](/demos/introduction) for more information on specific features. \ No newline at end of file diff --git a/guides/demos/introduction.mdx b/guides/demos/introduction.mdx deleted file mode 100644 index eb340072..00000000 --- a/guides/demos/introduction.mdx +++ /dev/null @@ -1,79 +0,0 @@ ---- -title: "Demo apps" -description: "Experience the power of Novu for triggering, digesting, and receiving notifications via different channels" ---- - -### 1. Email, In-App Notification & Authentication - - - - - - - - Find the source code here! - - - Try it yourself first-hand! - - - -### 2. Headless Notification Center - - - - - - - - Find the source code here! - - - Try it yourself first-hand! - - - -### 3. Notification Center - - - - - - - - Find the source code here! - - - Try it yourself first-hand! - - - -### 4. Email Digest Engine - - - - - - - - Find the source code here! - - - Try it yourself first-hand! - - - -### 5. InApp Digest Engine - - - - - - - - Find the source code here! - - - Try it yourself first-hand! - - diff --git a/guides/framework-guides/framework-remix.mdx b/guides/framework-guides/framework-remix.mdx deleted file mode 100644 index e8c1ef5d..00000000 --- a/guides/framework-guides/framework-remix.mdx +++ /dev/null @@ -1,319 +0,0 @@ ---- -title: 'How to send notifications with Remix and React email' -description: 'Learn how to send email notifications with Remix, React email and Novu' ---- - -# Introduction - -Learn how to send notifications with Remix, React email and Novu. You can check out the complete code for a [working app](https://github.com/novuhq/novu-framework-remix-example). - -## Prerequisites -- A Novu account -- Node installed on your machine -- A working Remix app - -## Follow these Steps - -### 1. Install all dependencies including react email components - -```jsx - npm install @novu/framework @react-email/components react-email -``` - -### 2. Integrate Novu with Remix - -Within the `app/routes` directory, create an `api.novu.tsx` file. - -```jsx -// app/routes/api.novu.tsx - -import { serve } from "@novu/framework/remix"; -import { client, signUpWorkflow } from "~/novu/workflows"; - -const handler = serve({ - client: client, - workflows: [signUpWorkflow] - }); - -export { handler as action, handler as loader }; -``` - -### 3. Create an email template in your Remix app - -Within the `app` directory, create an `emails` folder and add an email template file to it. - -In this scenario, create a `vercel-invite-user.tsx` file and the code below to it: - -```ts -// app/emails/vercel-invite-user.tsx - -import { - Body, - Button, - Container, - Column, - Head, - Heading, - Hr, - Html, - Img, - Link, - render, - Row, - Section, - Text, - Tailwind, - } from "@react-email/components"; - - interface VercelInviteUserEmailProps { - username?: string; - userImage?: string; - invitedByUsername?: string; - invitedByEmail?: string; - teamName?: string; - teamImage?: string; - inviteLink?: string; - inviteFromIp?: string; - showJoinButton?: boolean; - inviteFromLocation?: string; - buttonText?: string - } - - const baseUrl = process.env.VERCEL_URL - ? `https://${process.env.VERCEL_URL}` - : ""; - - export const VercelInviteUserEmail = ({ - username, - userImage, - invitedByUsername, - invitedByEmail, - teamName, - teamImage, - inviteLink, - inviteFromIp, - inviteFromLocation, - showJoinButton, - buttonText - }: VercelInviteUserEmailProps) => { - - return ( - - - - - -
    - Vercel -
    - - Join {teamName} on Vercel - - - Hello {username}, - - - {invitedByUsername} ( - - {invitedByEmail} - - ) has invited you to the {teamName} team on{" "} - Vercel. - -
    - - - - - - invited you to - - - - - -
    - {showJoinButton && ( -
    - -
    - )} - - or copy and paste this URL into your browser:{" "} - - {inviteLink} - - -
    - - This invitation was intended for{" "} - {username}. This invite was - sent from {inviteFromIp}{" "} - located in{" "} - {inviteFromLocation}. If you - were not expecting this invitation, you can ignore this email. If - you are concerned about your account's safety, please reply to - this email to get in touch with us. - -
    - -
    - - ); - }; - - VercelInviteUserEmail.PreviewProps = { - username: "alanturing", - userImage: `${baseUrl}/static/vercel-user.png`, - invitedByUsername: "Alan", - invitedByEmail: "alan.turing@example.com", - teamName: "Enigma", - teamImage: `${baseUrl}/static/vercel-team.png`, - inviteLink: "https://vercel.com/teams/invite/foo", - inviteFromIp: "204.13.186.218", - inviteFromLocation: "São Paulo, Brazil", - } as VercelInviteUserEmailProps; - - export default VercelInviteUserEmail; - - export function renderEmail(input: any, payload: any) { - return render(); - } -``` -### 4. Create a Novu Workflow - -Next, create a Novu workflow with an email step. This code-first notification workflow approach makes it easy for product teams to modify notification content. - -Within the `app` directory, create an `novu` folder and add a `workflows.ts` file to it. Copy/paste the code below to the recently created file. - -```jsx -// app/novu/workflows.ts - -import { Client, workflow } from '@novu/framework'; -import { renderEmail } from '~/emails/vercel-invite-user'; - -export const client = new Client({ - - apiKey: process.env.NOVU_API_KEY, - /** - * Disable this flag only during local development - * For production this should be true - */ - strictAuthentication: process.env.NODE_ENV !== "development" -}); - -export const signUpWorkflow = workflow('new-signup', async ({ step, payload }) => { - // Send a welcome email - await step.email('send-email', async (inputs) => { - return { - subject: `Welcome to sending emails with Novu & Remix`, - body: renderEmail(inputs, payload), - }; - }, { - inputSchema: { - type: "object", - properties: { - showJoinButton: { type: "boolean", default: true }, - buttonText: { type: "string", default: "Join the team" }, - userImage: { - type: "string", - default: "https://react-email-demo-bdj5iju9r-resend.vercel.app/static/vercel-user.png", - format: "uri", - }, - invitedByUsername: { type: "string", default: "Alan" }, - invitedByEmail: { - type: "string", - default: "alan.turing@example.com", - format: "email", - }, - teamName: { type: "string", default: "Team Awesome" }, - teamImage: { - type: "string", - default: "https://react-email-demo-bdj5iju9r-resend.vercel.app/static/vercel-team.png", - format: "uri", - }, - inviteLink: { - type: "string", - default: "https://vercel.com/teams/invite/foo", - format: "uri", - }, - inviteFromIp: { type: "string", default: "204.13.186.218" }, - inviteFromLocation: { - type: "string", - default: "São Paulo, Brazil", - }, - }, - }, - }); - // JSON Schema for validation and type-safety. Zod, and others coming soon. -}, { payloadSchema: { properties: { text: { type: 'string' } } } }); -``` - -### 5. Preview Email Workflow & Sync to Novu Cloud - -Open Novu Dev Studio to preview and make changes to the email workflow as needed via the command below: - -```jsx -npx novu-labs@latest echo -``` - -1. Run the Studio - - -**Note:** Use the port on which your Remix app is running for the Bridge endpoint so that the Novu Dev Studio can connect to your API route as highlighted in the image above. - -2. Check out the signup email workflow and test - - - -3. Deploy to Novu Cloud when you're done. - -On the top right(as seen in the image above) of the Novu Dev Studio, you can sync to Novu Cloud when you're done working locally. - -**Note:** You'll need to create a local tunnel that the Novu Cloud environment can reach for local experimentation purposes. Ngrok is a good tunnel. - -### 6. Send a Notification - -Trigger a notification using the recently deployed workflow either via your [Novu Cloud dashboard](https://web.novu.co) or [code](/quickstarts/react#trigger-a-notification). - -```js -import { Novu } from "@novu/node"; - -const novu = new Novu(""); - -novu.trigger("new-signup", { - to: { - subscriberId: "789", - } -}); -``` -Once you've built the workflow, you might want read one of [our other guides](/guides/framework-guides/product) on how to empower product teams to manage notification workflows. \ No newline at end of file diff --git a/guides/framework-guides/otp.mdx b/guides/framework-guides/otp.mdx deleted file mode 100644 index d0acde68..00000000 --- a/guides/framework-guides/otp.mdx +++ /dev/null @@ -1,316 +0,0 @@ ---- -title: 'How to send OTP verification email notifications with React-email' -description: 'Learn how to send OTP verification email notifications with React-email in your NextJS app' ---- - -## Introduction - -In this guide, you’ll learn how to send OTP verification email notifications using the React-email package. Follow these steps: - -## Getting started - -Integrating Novu’s code-first workflow with React.Email for your Next.js application can be done in a few steps: - -1. Create a NextJS app and wait for the installation: - -```bash -npx create-novu-app@latest --api-key= - -// The command will ask you if you want to include react-email into your new project. -``` -2. Once this installation is complete, simply `cd` into the directory and start your app using the `npm run dev` command. - -## Using a code-first workflow - -1. Write an email template - To write an email template, you can look over some of the examples in the [React Email](https://react.email/examples) documentation to get inspiration. In our case, this is the template: -```ts -import { - Body, - Button, - Container, - Head, - Heading, - Hr, - Html, - Img, - Link, - Preview, - Section, - Text, - render -} from "@react-email/components"; -import * as React from "react"; - -interface LinearLoginCodeEmailProps { - validationCode?: string; - showJoinButton?: boolean; - buttonText?: string; - inviteLink?: string; - logoURL?: string; - inviteFromLocation?: string; - inviteFromIp?: string; - supportEmail?: string; -} - -export const LinearLoginCodeEmail = ({ - validationCode, - showJoinButton, - buttonText, - inviteLink, - logoURL, - inviteFromIp, - inviteFromLocation, - supportEmail -}: LinearLoginCodeEmailProps) => ( - - - Your login code for Linear - - - Linear - - Your login code for Linear - -
    - {showJoinButton && ( -
    - -
    - )} -
    - - This link and code will only be valid for the next 5 minutes. If the - link does not work, you can use the login verification code directly: - -
    - {validationCode}110658 -
    -
    - Not expecting this email? - - This invite was - sent from {inviteFromIp}{" "} - located in{" "} - {inviteFromLocation}. - - - Please contact{" "} - - {supportEmail} - {" "} - if you did not request this code. - -
    - - -); - -LinearLoginCodeEmail.PreviewProps = { - validationCode: "tt226-5398x", -} as LinearLoginCodeEmailProps; - -export default LinearLoginCodeEmail; - -const logo = { - borderRadius: 21, - width: 42, - height: 42, -}; - -const main = { - backgroundColor: "#ffffff", - fontFamily: - '-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif', -}; - -const container = { - margin: "0 auto", - padding: "20px 0 48px", - maxWidth: "560px", -}; - -const secondary = { - color: "#000", - display: "inline-block", - fontFamily: "HelveticaNeue-Medium,Helvetica,Arial,sans-serif", - fontSize: "20px", - fontWeight: 500, - lineHeight: "24px", - marginBottom: "0", - marginTop: "2rem", - textAlign: "center" as const, -}; - -const paragraphSupportText = { - fontSize: "15px", - fontWeight: "700", -}; - -const paragraph = { - margin: "0 0 15px", - fontSize: "15px", - lineHeight: "1.4", - color: "#3c4149", -}; - -const buttonContainer = { - padding: "27px 0 27px", -}; - -const button = { - backgroundColor: "#5e6ad2", - borderRadius: "3px", - fontWeight: "600", - color: "#fff", - fontSize: "15px", - textDecoration: "none", - textAlign: "center" as const, - display: "block", - padding: "11px 23px", -}; - -const hr = { - borderColor: "#dfe1e4", - margin: "42px 0 26px", -}; - -const code = { - color: "#000", - display: "inline-block", - fontFamily: "HelveticaNeue-Bold", - fontSize: "16px", - fontWeight: 700, - letterSpacing: "6px", - lineHeight: "40px", - paddingBottom: "8px", - paddingTop: "8px", - margin: "0 auto", - width: "100%", - textAlign: "center" as const, -}; - -const paragraphSupport = { - color: "#444", - fontSize: "15px", - fontFamily: "HelveticaNeue,Helvetica,Arial,sans-serif", - letterSpacing: "0", - lineHeight: "23px", - padding: "0 40px", - margin: "0", - textAlign: "center" as const, -}; - -const link = { - color: "#444", - textDecoration: "underline", -}; - -const codeContainer = { - background: "rgba(0,0,0,.05)", - borderRadius: "4px", - margin: "16px auto 14px", - verticalAlign: "middle", - width: "280px", -}; - -export function renderOTPEmail(payload: any) { - return render(); -} -``` - -2. Launch Dev Studio - Novu’s code-first approach lets you see how the template would look when rendered, right when defining it, using the Dev Studio. To launch the dev studio locally you can run `npx novu-labs@latest echo`. The Dev Studio will be started by default on port 2022, and accessible via: http://localhost:2022 - - - -3. Define a workflow that uses that template to send notifications - In this step, we need to define a workflow that uses the template we wrote above to render the email notification: -```ts -import { Client, workflow } from "@novu/framework"; -import { renderOTPEmail } from "./otp"; - -export const client = new Client({ - apiKey: process.env.NOVU_API_KEY, - /** - * Disable this flag only during local development - */ - strictAuthentication: process.env.NODE_ENV !== "development", -}); - -export const otpFlow = workflow('otp-flow', async ({ step, payload }) => { - // Send a welcome email - await step.email('send-email', async (inputs) => { - return { - subject: `Here's your verification code, Sumit!`, - body: renderOTPEmail(inputs, payload), - }; - }, { - inputSchema: { - type: "object", - properties: { - showJoinButton: { type: "boolean", default: true }, - buttonText: { type: "string", default: "Login to Linear" }, - logoURL: { - type: "string", - default: "https://react-email-demo-7qy8spwep-resend.vercel.app/static/linear-logo.png", - format: "uri", - }, - supportEmail: { type: "string", default: "support@linear.co" }, - validationCode: { type: "string", default: "tt226-5398x" }, - inviteLink: { - type: "string", - default: "https://linear.app", - format: "uri", - }, - inviteFromIp: { type: "string", default: "204.13.186.218" }, - inviteFromLocation: { - type: "string", - default: "São Paulo, Brazil", - }, - }, - }, - }); - // JSON Schema for validation and type-safety. Zod, and others coming soon. -}, { payloadSchema: { properties: { text: { type: 'string' } } } }); -``` -4. Triggering the workflow - Lastly, we need to trigger the workflow we created above. Here’s how to trigger it: -```ts -import { Novu } from '@novu/node'; - -const novu = new Novu(''); - -export async function POST(request: Request) { - const res = await request.json(); - - await novu.trigger('otp-flow', { - to: { - subscriberId: 'new-user', - }, - payload: { - email: res.email, - username: res.username, - }, - }); - - console.log('triggered') - return Response.json({ success: true }); -} -``` -When we trigger this workflow, here’s the email received on the client-side: - -That’s it! - -That’s how you create and use an OTP workflow. You can check out [our docs](https://docs.novu.co/guides/framework-guides/framework-react-email) for a hands on guide with more in-depth instructions. - -Once you've built the workflow, you might want read one of [our other guides](/guides/framework-guides/product) on how to empower product teams to manage notification workflows. - -Don’t forget to share your workflows with us and as always, hit us up on Discord with any questions you might have! \ No newline at end of file diff --git a/guides/how-to-integrate-segment-with-novu.mdx b/guides/integrations/segment.mdx similarity index 76% rename from guides/how-to-integrate-segment-with-novu.mdx rename to guides/integrations/segment.mdx index cdb2ce78..049d8b68 100644 --- a/guides/how-to-integrate-segment-with-novu.mdx +++ b/guides/integrations/segment.mdx @@ -1,13 +1,13 @@ --- -title: 'How To Integrate Segment With Novu' -description: '' +title: "How To Integrate Segment With Novu" +sidebarTitle: "Integrate with Segment" --- [Segment](https://segment.com/) is a customer data platform that helps businesses collect, manage, and analyze customer data from multiple sources. It streamlines data integration, ensures data quality, and aids in compliance with regulations. Segment's tools enable better understanding of customer behavior and facilitate personalized customer experiences. It's useful for marketing, analytics, and product teams in diverse industries. If this is your first time working with the tool, consider consulting the 'Get Started' guide in [Segment's official documentation](https://segment.com/docs/getting-started/). -## Examples Use Case +## Examples Use Case **Enhancing Customer Engagement and Conversion Rates** @@ -16,12 +16,12 @@ If this is your first time working with the tool, consider consulting the 'Get S - The e-commerce platform uses Segment to collect and unify customer data from various sources like website visits, purchase history, and customer interactions. - Segment helps in creating a unified customer profile by consolidating data from different touchpoints – mobile app usage, website browsing patterns, and previous purchase behavior. - @@ -30,15 +30,18 @@ If this is your first time working with the tool, consider consulting the 'Get S - The data collected is analyzed to understand customer preferences, buying patterns, and engagement levels. - Customers are segmented into different groups based on their behavior, interests, and purchase history. For instance, one segment might include frequent shoppers, while another might focus on those who browse but rarely purchase. - + + - Novu is used to craft and send personalized notifications to these segmented groups. - For frequent shoppers, notifications about new arrivals or exclusive deals might be sent. For those who browse but rarely buy, notifications could include special discount codes or reminders about items left in the cart. @@ -62,7 +65,6 @@ frameBorder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media - **Outcome:** - Enhanced customer engagement through personalized and timely notifications. @@ -78,17 +80,26 @@ As of today Segment doesn’t have We will be using [Destination Functions](https://segment.com/docs/connections/functions/destination-functions/). Destination functions allow you to transform and annotate your Segment events and send them to any external tool or API without worrying about setting up or maintaining any infrastructure. ### Create a destination function + - From your workspace, go to `Connections > Catalog` and click the `Functions` tab. - Click `New Function`. - Select `Destination` as the function type and click `Build`. - After you click `Build`, a code editor appears. Use the editor to write the code for your function, configure settings, and test the function’s behavior. -Want to see some example functions? Check out the templates available in the Functions UI, or in the open-source Segment Functions Library. + + Want to see some example functions? Check out the templates available in the + Functions UI, or in the open-source Segment Functions Library. + ### Code the destination function + Segment invokes a separate part of the function (called a “handler”) for each event type that you send to your destination function. - Your function isn’t invoked for an event if you’ve configured a destination filter, and the event doesn’t pass the filter. + + {" "} + Your function isn’t invoked for an event if you’ve configured a destination filter, + and the event doesn’t pass the filter.{" "} + The default source code template includes handlers for all event types. You don’t need to implement all of them - just use the ones you need, and skip the ones you don’t. @@ -105,7 +116,7 @@ Destination functions can define handlers for each message type in the Segment s Each of the functions above accepts two arguments: -`event` - Segment event object, where fields and values depend on the event type. +`event` - Segment event object, where fields and values depend on the event type. For example, in “Identify” events, Segment formats the object to match the Identify spec. `settings` - Set of settings for this function. @@ -113,26 +124,28 @@ The example below shows a destination function that listens for “Track” even ```javascript async function onTrack(event) { - await fetch('https://example-service.com/api', { - method: 'POST', + await fetch("https://example-service.com/api", { + method: "POST", headers: { - 'Content-Type': 'application/json' + "Content-Type": "application/json", }, body: JSON.stringify({ event_name: event.event, event_properties: event.properties, - timestamp: event.timestamp - }) - }) + timestamp: event.timestamp, + }), + }); } ``` -To change which event type the handler listens to, you can rename it to the name of the message type. + +To change which event type the handler listens to, you can rename it to the name of the message type. For example, if you rename this function `onIdentify`, it listens for “Identify” events instead. -If you you need an example of Novu destination function, you can find it in [here](https://github.com/novuhq/segment-novu-destination-scripts). -Feel free to submit your function examples as well! + If you you need an example of Novu destination function, you can find it in + [here](https://github.com/novuhq/segment-novu-destination-scripts). Feel free + to submit your function examples as well! ### Sending a payload to Novu @@ -148,54 +161,56 @@ To successfully send a payload to Novu, triggering the appropriate [workflow](/g ```javascript async function onTrack(event, settings) { - const eventProperties = event.properties; - - if (!eventProperties.environment) { - throw new InvalidEventPayload('environment property is required'); - } - if (!eventProperties.name) { - throw new InvalidEventPayload('Workflow name property is required'); - } - - console.log('environment:', eventProperties.environment); - - const payload = { - name: eventProperties.name, - to: { - subscriberId: eventProperties.user.subscriberId, - email: eventProperties.user.email, - firstName: eventProperties.user.firstName, - lastName: eventProperties.user.lastName - }, - payload: { eventProperties } - }; - const endpoint = settings.novuEndpoint; - console.log('endpoint:', endpoint); - - const ApiKey = settings.novuApiKey; // Your NOVU API key - if (eventProperties.environment === settings.environment) { - let response; - try { - response = await fetch(endpoint, { - method: 'POST', - headers: { - Authorization: `ApiKey ${ApiKey}`, - 'Content-Type': 'application/json' - }, - body: JSON.stringify(payload) - }); - } catch (error) { - // Retry on connection error - throw new RetryError(error.message); - } - if (response.status >= 500 || response.status === 429) { - // Retry on 5xx (server errors) and 429s (rate limits) - throw new RetryError(`Failed with ${response.status}`); - } - } + const eventProperties = event.properties; + + if (!eventProperties.environment) { + throw new InvalidEventPayload("environment property is required"); + } + if (!eventProperties.name) { + throw new InvalidEventPayload("Workflow name property is required"); + } + + console.log("environment:", eventProperties.environment); + + const payload = { + name: eventProperties.name, + to: { + subscriberId: eventProperties.user.subscriberId, + email: eventProperties.user.email, + firstName: eventProperties.user.firstName, + lastName: eventProperties.user.lastName, + }, + payload: { eventProperties }, + }; + const endpoint = settings.novuEndpoint; + console.log("endpoint:", endpoint); + + const ApiKey = settings.novuApiKey; // Your NOVU API key + if (eventProperties.environment === settings.environment) { + let response; + try { + response = await fetch(endpoint, { + method: "POST", + headers: { + Authorization: `ApiKey ${ApiKey}`, + "Content-Type": "application/json", + }, + body: JSON.stringify(payload), + }); + } catch (error) { + // Retry on connection error + throw new RetryError(error.message); + } + if (response.status >= 500 || response.status === 429) { + // Retry on 5xx (server errors) and 429s (rate limits) + throw new RetryError(`Failed with ${response.status}`); + } + } } ``` + #### Create Settings and Secrets + Segment's settings allow you to pass configurable variables to your function, which is the best way to pass sensitive information such as security tokens. For example, you might use settings as placeholders to use information such as an API endpoint and API key. You can learn more about it [here](https://segment.com/docs/connections/functions/destination-functions/#create-settings-and-secrets). @@ -216,6 +231,7 @@ Now, while configuring the function, Add these three variables to be then used i #### Test the destination function + You can test your code directly from the editor in two ways: **1. Use sample events for testing** @@ -235,6 +251,7 @@ You can also manually include your own JSON payload of a Segment event, instead **Here is the the event example JSON we will use** + ```JSON { "type": "track", @@ -258,21 +275,14 @@ We will now execute the function based on the manual event input we've provided. -Whether you chose the first or second option to test your Destination Function, you can also check if the function triggered your workflow and whether it was delivered through Novu's [Activity Feed](https://web.novu.co/activities?utm_campaign=docs-guides-segment). +Whether you chose the first or second option to test your Destination Function, you can also check if the function triggered your workflow and whether it was delivered through Novu's [Activity Feed](https://dashboard.novu.co/activities?utm_campaign=docs-guides-segment). - This guide was inspired by the following resources: + - https://discord.com/channels/895029566685462578/1060308007658987571/1077377322547687475 - https://segment.com/docs/getting-started/05-data-to-destinations/ - https://aviyel.com/post/3381/how-to-integrate-novu-with-segment - - - - - - - diff --git a/guides/novu-fcm-web.mdx b/guides/novu-fcm-web.mdx deleted file mode 100644 index 7cbb35f5..00000000 --- a/guides/novu-fcm-web.mdx +++ /dev/null @@ -1,218 +0,0 @@ ---- -title: 'How to send web push notifications with FCM and Novu' -description: 'Send FCM push notifications in a React app using Novu' ---- - -## Introduction -In this guide, we'll learn how to send FCM push notifications in a web app using Novu. But before exploring the actual code, let’s understand what a push notification is and how it works. -You can find the [frontend code](https://github.com/novuhq/fcmWebPushFE) as well as [backend code](https://github.com/novuhq/FCMWebPushBE) for the app on Github. -
    -Push notifications are notifications that are sent to a user's devices whether the user is using the app or not. We'll be using Firebase Cloud Messaging (FCM) integration of Novu to send these notifications. This guide will be broken down into three parts: -1. Setting up Novu. -2. Setting up Firebase. -3. Adding Firebase to the Frontend. -4. Adding Novu to the Backend. - -## Setting up Firebase -1. Create a Firebase account if you don't already have one. Then, create a new project and all the relevant details. - -2. Once your project has been created, you'll be greeted with this welcome screen and asked to add Firebase to your app, so let's do that. - -3. We'll choose the web option because we're creating a web app. - -4. Give your app a name and click on the 'Register' button. - -5. We're done with the Firebase setup! Now, we'll set up Novu and finally proceed to add both (Novu and FCM) to our app. - -## Setting up Novu -We'll need a workflow to trigger notifications to our user's devices. Follow the steps below to create one to ensure we are on the path to make FCM work as expected: -1. Head over to the [Integrations Store](https://web.novu.co/integrations?utm_campaign=docs-guides-fcm-web) on the Novu Web Dashboard and make sure that you've turned on the 'Firebase Cloud Messaging' integration. - -If you're doing this for the first time, you'll need to get your service account key from the [Firebase Console](https://console.firebase.google.com/). Read more about it in our [FCM provider docs.](https://docs.novu.co/channels-and-providers/push/fcm) -2. Now, we need to create a workflow that we'll trigger from our app to send notifications. Head over to [workflows in the Novu Web Dashboard](https://web.novu.co/workflows?utm_campaign=docs-guides-fcm-web) and click on the 'Add a workflow' button. - -3. Choose the 'blank workflow' option from the dropdown menu. - -3. Drag and drop the 'Push' node below the Workflow Trigger node. - -4. If you hover over the newly added 'Push' node, you'll see an error saying 'Message content and title are missing'. - -5. We're going to use the identifiers `title` and `body` for the title and the content of notifications, so let's add them in the 'Push message title' and the 'Push message content' fields respectively. So, add them and give your workflow a suitable name. - -6. Now, click on the 'get snippet' button to get the trigger code. - - -## Adding Firebase to the frontend -1. In your project, add Firebase using the following command: -```bash -npm install firebase -``` -2. Create a new file called `firebase.js` and add the following to it: -```jsx -import { initializeApp } from "firebase/app"; -import { getMessaging, onMessage } from "firebase/messaging"; - -//from firebase console -// Your web app's Firebase configuration -const firebaseConfig = { - apiKey: import.meta.env.VITE_FIREBASE_API_KEY, - authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN, - projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID, - storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET, - messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID, - appId: import.meta.env.VITE_FIREBASE_APP_ID, -}; - -// Initialize Firebase -export const app = initializeApp(firebaseConfig); - -// Initialize Firebase Cloud Messaging and get a reference to the service -export const messaging = getMessaging(app); - -export const onMessageListener = () => - new Promise((resolve) => { - onMessage(messaging, (payload) => { - console.log("payload", payload) - resolve(payload); - }); - }); -``` -3. Now, we need to generate tokens using the `getToken` method provided by Firebase. But we want to use this only when the user has allowed notifications on their end. So let's request notification permission from the user and if granted, we'll generate the token. Add this to your root module: -```jsx - async function requestPermission() { - const permission = await Notification.requestPermission() - if (permission === 'granted') { - // get token - const token = await getToken(messaging, { vapidkey: import.meta.env.VITE_VAPID_KEY }) - } else if (permission === 'denied') { - alert('Persmission denied!') - } - } - useEffect(() => { - requestPermission(); - }, []) -``` -4. To use the `getToken` method, you need a Voluntary Application Server Identification or `VAPID key`. Go to your Project Settings -> Cloud Messaging -> Generate key pair (under Web Push Certificates): - -5. In order for Firebase's background listener service to work, you need a service worker. Make sure you've created a service worker file `firebase-messaging-sw.js` and add the following to it: -```jsx -importScripts("https://www.gstatic.com/firebasejs/8.10.0/firebase-app.js"); -importScripts( - "https://www.gstatic.com/firebasejs/8.10.0/firebase-messaging.js" -); - -const firebaseConfig = { - apiKey: import.meta.env.VITE_FIREBASE_API_KEY, - authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN, - projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID, - storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET, - messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID, - appId: import.meta.env.VITE_FIREBASE_APP_ID, -}; - -firebase.initializeApp(firebaseConfig); -const messaging = firebase.messaging(); -messaging.onBackgroundMessage((payload) => { - console.log( - "notif incoming", - payload - ); - const notificationTitle = JSON.parse(JSON.parse(payload.notification)).title - // payload.notification.title; - const notificationOptions = { - body: payload.notification.body, - icon: payload.notification.image, - }; - - self.registration.showNotification(notificationTitle, notificationOptions); -}); - - -``` - -## Adding Novu to the backend -In your app's backend, add Novu using the following command: -```bash -npm install @novu/node -``` -Now, create a route that you want to hit when called from the front end. In our app, this is the route: -```jsx -import express from "express"; -import { createNotif } from '../controller/notif.js' - -const router = express.Router(); - -router.post('/create', createNotif) - -export default router; -``` - -Now, we need a controller function to handle the logic for what is to be sent in the trigger function’s payload. In our case, this is the controller function: -```jsx -import { inAppNotification } from "../novu/novu.js"; - -export const createNotif = async (req, res) => { - - const { title, body } = req.body - try { - await inAppNotification(title, body); - res.status(201).json({ message: 'success', title: title, body: body }); - } catch (error) { - res.status(409).json({ message: error.message }); - } -} -``` - -To make it modular, we’ll keep the trigger code in a separate function in a separate file (`novu.js`, in our case) and the trigger function is getting called in the controller function above by the name `createNotif`. - -If you’re following the guide, you should already have the trigger function. But before we can add it to our app, we need one key thing - **Subscribers.** - -[Subscribers](https://docs.novu.co/subscribers/subscribers) are entities to which the notifications are sent. You can see a list of subscribers in the [Novu dashboard](https://web.novu.co/subscribers?utm_campaign=docs-guides-fcm-web) as well. - -We'll create a subscriber in our app. Our backend will be written in Node.js, but we also have backend [SDKs in PHP, .NET, Go, Ruby, Python, and Kotlin](/sdks/introduction). The recommended way to create a subscriber in NodeJS is as follows: -```jsx - await novu.subscribers.identify(process.env.NOVU_SUB_ID, { - firstName: "pushSubscriber" - }); -``` -Here, we're creating a subscriber with the `subscriberID` of whatever value the `env` file contains for the identifier `NOVU_SUB_ID`. You can read more about subscribers [in our docs.](/subscribers/subscribers). - -Back in our app, before we can now add the trigger function, we need to set device identifiers using the [setCredential method](https://docs.novu.co/channels-and-providers/push/fcm#set-device-token): -```jsx -await novu.subscribers.setCredentials(process.env.NOVU_SUB_ID, PushProviderIdEnum.FCM, { - deviceTokens: [process.env.DEVICE_TOKEN], - }); -``` - -You'll need to pass the `subscriberID` and the `deviceTokens` in the `setCredentials` method. - -Finally, we can add the trigger code: -```jsx -import { Novu, PushProviderIdEnum } from '@novu/node'; - -export const inAppNotification = async (title, body) => { - - const novu = new Novu(process.env.NOVU_API_KEY); - await novu.subscribers.identify(process.env.NOVU_SUB_ID, { - firstName: "pushSubscriber" - }); - - await novu.subscribers.setCredentials(process.env.NOVU_SUB_ID, PushProviderIdEnum.FCM, { - deviceTokens: [process.env.DEVICE_TOKEN], - }); - - novu.trigger(process.env.NOVU_WORKFLOW_ID, { - to: { - subscriberId: process.env.NOVU_SUB_ID - }, - payload: { - title: title, - body: body - }, - }); -}; -``` - -We're done with the app. You can find the [frontend code](https://github.com/novuhq/fcmWebPushFE) as well as [backend code](https://github.com/novuhq/FCMWebPushBE) for the app on Github. - -Congratulations on following the guide up until this point. If you’ve done everything as recommended, you’ll end up with an app that uses Firebase Cloud Messaging to send notifications to your users using Novu. \ No newline at end of file diff --git a/guides/slack-guide.mdx b/guides/slack-guide.mdx deleted file mode 100644 index 10e23479..00000000 --- a/guides/slack-guide.mdx +++ /dev/null @@ -1,187 +0,0 @@ ---- -title: 'How to use Novu to send notifications to a Slack channel' -description: 'Learn to send Slack notifications swiftly with Novu (manually managed)' ---- - -# Introduction - -In this guide, you'll learn how to use Novu to send notifications directly to a Slack channel. But before coding anything up, we first need to go through a setup process. -The corresponding docs for this guide are available on our [docs](https://docs.novu.co/channels-and-providers/chat/slack). -The entire code of this app (frontend as well as backend) can be found [here](https://github.com/novuhq/slack-app). - -So let's begin! - -# Create a Slack App -Creating a Slack app is fairly simple. Follow these steps to create your app: -1. Go to [Slack's app dashboard](https://api.slack.com/apps) and click on the 'Create new App' button, as shown in the image: - -2. Choose 'From Scratch' from the following dialog: - -3. Choose a name for your app and select the Slack workspace in which you want to send notifications: - -4. Once you're done, simply click the 'Create App' button: - -5. Once done, you'll be greeted with the screen shown below. We'll make a couple of changes here and it'll be ready to go. - -6. We'll need `Client Id` from the [Slack Developer's Dashboard](https://api.slack.com/apps) for configuring Slack Integration in the [Novu Web Dashboard](https://web.novu.co/integrations?utm_campaign=docs-guides-slack) later, so keep it handy. - - -# Create a workflow in the Novu Web Dashboard -1. To create a workflow, head to the workflow section in the [Novu Web Dashboard](https://web.novu.co/workflows?utm_campaign=docs-guides-slack). -2. Click on the 'Add a workflow' button and select 'Blank workflow' from the dropdown. - -3. Once there, give your workflow a name and drag and drop the 'chat' option below the 'workflow trigger' step. - -4. You can also add variables in the Workflow Editor. For example, here I've added 'chatMsg' as a variable as I'll be sending data using it. - -Whatever is placed inside double braces is a variable. -5. Make sure that you've turned on the Slack integration in the [Integrations Store](https://web.novu.co/integrations?utm_campaign=docs-guides-slack). - -6. To turn the Slack integration on, you'll need `Client Id`. You should have it already but if you don't, you can obtain it from the [Slack Developer's Dashboard](https://api.slack.com/apps). - -7. Once you have it, you need to plug it into the respective field in the [Slack Integration Settings](https://web.novu.co/integrations?utm_campaign=docs-guides-slack) on the Novu Web Dashboard. - - -# Create the backend -The backend for this app is quite simple. Simply install the Novu package: -```bash -npm install @novu/node - -``` -Now, create a route that you'll hit when called from the front end. We'll also need to add it to our Slack app (discussed below). For our demo app, this is the route I've created: -```jsx -import express from "express"; -import { chatController } from "../controller/chat.js"; - -const router = express.Router(); - -router.post("/sendChat", chatController); - -export default router; -``` -Now, we need a controller function to handle what is to be sent in the trigger's function payload. Here's the controller I wrote: -```jsx -import { chat } from "../novu/novu.js" - -export const chatController = async (req, res) => { - const { chatMsg } = req.body; - try { - await chat(chatMsg); - res.status(201).json({ message: "Message sent successfully" }); - } catch (error) { - res.status(500).json({ message: error.message }) - } -} -``` -Notice how we're expecting 'chatMsg' in our payload. This is why we added it as a variable in the workflow created earlier. - -To make it modular, we’ll keep the trigger code in a separate function in a separate file, `novu.js`, in our case, which is as follows: -```jsx -import { Novu, ChatProviderIdEnum } from '@novu/node'; - -export const chat = async (chatMsg) => { - const novu = new Novu(process.env.YOUR_NOVU_API_KEY_HERE); - await novu.subscribers.identify(process.env.SUB_ID, { - firstName: 'newSubForSlackChat', - }); - - await novu.trigger('slack', { - to: { - subscriberId: process.env.SUB_ID - }, - payload: { - chatMsg: chatMsg - } - }); -} -``` -In this code snippet above, we're first initializing a new instance of Novu, then using it to 'identify' or [create](https://docs.novu.co/subscribers/subscribers#create-a-subscriber) a [subscriber](https://docs.novu.co/subscribers/subscribers). -The 'identify' method tries to find a subscriber with the given info. If it can't find any such subscriber, it creates a new subscriber with the supplied info. -After creating the subscriber, it runs the trigger code for the workflow we had created. You can find the trigger workflow by clicking on the 'Get Snippet' button on the workflow: - - - -# Configure the Slack app - -There are two ways to configure the Slack app. One is the Novu managed option and another the manually managed option. -Since the Novu managed is fairly straightforward, I'll demonstrate the manual method here. Follow along to set it up! - -1. Goto 'Incoming Webhooks' in your Slack app settings and turn it on. - -2. Click on the 'Add New Webhook to Workspace': - -3. Now, go ahead and select the channel in which you want to send notifications and click 'allow'. - -4. Then, copy the 'webhookUrl' from Slack. - -5. Now, add the code below to the backend trigger functionality. Using this code, we are specifying what `webhookUrl` Novu will use to authenticate when using the Slack provider. - - - -```javaScript -import { - Novu, - ChatProviderIdEnum -} from '@novu/node'; - -const novu = new Novu(""); - -await novu.subscribers.setCredentials('subscriberId', ChatProviderIdEnum.Slack, { - webhookUrl: "", -}); -``` - - -```bash -curl -L -X PUT 'https://api.novu.co/v1/subscribers//credentials' \ --H 'Content-Type: application/json' \ --H 'Accept: application/json' \ --H 'Authorization: ApiKey ' \ --d '{ - "providerId": "slack", - "credentials": { - "webhookUrl": "" - }, - "integrationIdentifier": "slack-MnGLxp8uy" -}' -``` - - - -In our case, we'll add it to the `Novu.js` file we created earlier so that the final file becomes : -```jsx -import { Novu, ChatProviderIdEnum } from '@novu/node'; - -export const chat = async (chatMsg) => { - const novu = new Novu(process.env.YOUR_NOVU_API_KEY_HERE); - await novu.subscribers.identify(process.env.SUB_ID, { - firstName: 'newSubForSlackChat', - }); - - await novu.subscribers.setCredentials(process.env.SUB_ID, ChatProviderIdEnum.Slack, { - webhookUrl: process.env.SLACK_WEBHOOK_URL, - }); - - await novu.trigger('slack', { - to: { - subscriberId: process.env.SUB_ID - }, - payload: { - chatMsg: chatMsg - } - }); -} -``` -6. Finally, we'll add the route we'd created earlier to the redirect URL section, as shown: - -For demonstration purposes, I've added both the local as well as the deployed URL. You only need one depending on whether you're running this locally or have a deployed version. -Now, our backend is all done, our Slack is all setup and all we need to do is hit the URL - -# Front end set up - -For our demonstration purposes, the front end is pretty basic. All we have to do is have an input field to take the notification text in and send the payload. The front end code for this demo app is available on our [Github repo](https://github.com/novuhq/slack-app/tree/main/frontend). -And here's our app in all its glory! - - - -This is how we send Slack notifications using Novu! \ No newline at end of file diff --git a/guides/usecases/introduction.mdx b/guides/usecases/introduction.mdx deleted file mode 100644 index 8fa7c2e5..00000000 --- a/guides/usecases/introduction.mdx +++ /dev/null @@ -1,28 +0,0 @@ ---- -title: "Learn by usecase" -description: "Learn Novu's Use Cases" ---- - -Novu is built to empower engineering teams in integrating rich product notification experiences seamlessly without re-inventing the wheel. - -It has been thoroughly and beautifully crafted to help individuals, teams, businesses and organizations focus on what truly matters: creating exceptional user experiences and driving engagement through smooth and swift notifications delivery. - -The common use cases for using Novu are highlighted below: - - - - - - - - - - - - - - - - - - diff --git a/guides/usecases/multiple-delivery-providers.mdx b/guides/usecases/multiple-delivery-providers.mdx deleted file mode 100644 index f6d6893c..00000000 --- a/guides/usecases/multiple-delivery-providers.mdx +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: "Managing multiple delivery providers in one place" -description: "Learn to manage many delivery providers across different channels in one dashboard" ---- - -[Novu's dashboard](https://web.novu.co/integrations?utm_campaign=docs-usecases-multipledelivery) empowers you to manage all your notification delivery providers across Email, Sms, Push and Chat in one place. - -You can activate, deactivate, modify settings, set primary and secondary active providers as you wish from the dashboard. - - -### Integrations Store - - - - - -### Add a new Delivery Provider - - - - diff --git a/guides/workflows/feedback-reviews.mdx b/guides/workflows/feedback-reviews.mdx new file mode 100644 index 00000000..a6ed0203 --- /dev/null +++ b/guides/workflows/feedback-reviews.mdx @@ -0,0 +1,66 @@ +--- +title: "Feedback & Reviews" +description: "Grow your business faster by asking users to leave a positive review" +--- + +## Intro + +Feedback and reviews are the lifeblood of any business. Setting up an automated workflow to do so however, is unnecessarily complicated by the need to implement notification logic like delayed send. + +[Explore the source code on GitHub](https://github.com/novuhq/examples/tree/main/novu-workflows/react/feedback-reviews) + +## Preview + + + +## Code Example + +```javascript +import { workflow } from '@novu/framework'; +import { renderFeedbackEmail } from '../emails/feedback'; +import { zodControlSchema, zodDelayControlSchema, zodPayloadSchema } from './schemas'; + +export const AirbnbReview = workflow( + "Airbnb Review", + async ({ step, payload }) => { + /** + * Delay Step + */ + await step.delay( + 'delay-1-week', + async (controls) => { + return { + type: "regular", + unit: controls.unit, // 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'months' + amount: controls.amount, // the number of units to delay workflow execution for + }; + }, + { + controlSchema: zodDelayControlSchema + } + ); + + /** + * Email Step + */ + await step.email( + "send-email", + async (controls) => { + return { + subject: controls.emailSubject, + body: renderFeedbackEmail(controls, payload), + }; + }, + { + controlSchema: zodControlSchema + } + ); + }, + { + payloadSchema: zodPayloadSchema + }, +); +``` \ No newline at end of file diff --git a/guides/workflows/introduction.mdx b/guides/workflows/introduction.mdx new file mode 100644 index 00000000..ae0af365 --- /dev/null +++ b/guides/workflows/introduction.mdx @@ -0,0 +1,34 @@ +--- +title: "Overview" +description: "You shouldn't have to reinvent the wheel. Here we share how to implement several real-world use cases of notifications workflows." +--- + +## Workflow Examples + +This directory consists of drop-in working projects of different types of notification workflows you can use. + +Each directory is a batteries-included Novu Nextjs app that you can simply clone, install and run! + + + + + Send One-Time Passwords (OTP) without worrying about email reputation scores. + + + Let users "Reset Password" or "Forgot Username" + + + Suspicious activity? Inform your users of recent login attempts + + + Send invoices or receipts after a transaction is processed. + + + Let buyers know when their order has shipped, complete with tracking number. + + + Reviews are the lifeblood of any business. Remind and incentivize your buyers to leave a review + + + + diff --git a/guides/workflows/invoice-receipt.mdx b/guides/workflows/invoice-receipt.mdx new file mode 100644 index 00000000..d59e6d7f --- /dev/null +++ b/guides/workflows/invoice-receipt.mdx @@ -0,0 +1,50 @@ +--- +title: "Invoice Receipt" +description: "Send invoices or receipts immediately after a user purchases" +--- + +## Intro + +Invoices and receipts require custom fields such as the order number, billing details, and objects purchased which must be dynamically fetched from the database. The use of step controls and the payload schema simplifies the fetching of that unique data. + +[Explore the source code on GitHub](https://github.com/novuhq/examples/tree/main/novu-workflows/react/invoice-receipts) + +## Preview + + + + +## Code Example + +```javascript +import { workflow } from '@novu/framework'; +import { renderAppleReceiptEmail } from '../emails/apple-receipt'; +import { zodControlSchema, jsonSchema, zodPayloadSchema } from './schemas'; + +/** + * Apple Receipt Workflow + */ +export const appleReceipt = workflow( + "Apple Receipt", + async ({ step, payload }) => { + await step.email( + "send-email", + async (controls) => { + return { + subject: controls.receiptSubject, + body: renderAppleReceiptEmail(controls, payload), + }; + }, + { + controlSchema: zodControlSchema + }, + ); + }, + { + payloadSchema: zodPayloadSchema + } +); +``` \ No newline at end of file diff --git a/guides/workflows/otp.mdx b/guides/workflows/otp.mdx new file mode 100644 index 00000000..1058251c --- /dev/null +++ b/guides/workflows/otp.mdx @@ -0,0 +1,81 @@ +--- +title: "OTP (One-Time Password)" +description: "Send OTPs without setting up your own infrastructure" +--- + +## Intro + +Sending One-Time Passwords (OTP) is often unnecessarily complicated, requiring engineering teams to waste valuable development cycles setting up SMS infrastructure and getting email reputation satisfactory for sending six digit passcodes. This three channel workflow example shows how to send an email, push notification, or SMS OTP. + +[Explore the source code on GitHub](https://github.com/novuhq/examples/tree/main/novu-workflows/react/otp) + +## Preview + +Send an email + + +Send a mobile push notification + + +Send an SMS notification + + + +## Code Sample + +```javascript +import { workflow } from '@novu/framework'; +import { renderOtpEmail } from '../emails/slack-otp'; +import { zodControlSchema, zodPayloadSchema, zodPushControlSchema, zodSmsControlSchema } from './schemas'; + +export const SlackVerificationOTP = workflow( + "Slack Verify OTP", + async ({ step, payload }) => { + await step.email( + "send-email", + async (controls) => { + return { + subject: controls.emailSubject, + body: renderOtpEmail(controls, payload), + }; + }, + { + controlSchema: zodControlSchema + }); + + // -----------------------------------push flow------------------------------------------------------------------------- + await step.push('send-push', async (controls) => { + return { + subject: controls.pushNotificationSubject, + body: `Your verification code from Slack is ${payload.validationCode}`, + }; + }, + { + controlSchema: zodPushControlSchema + }); + + // -----------------------------------sms flow------------------------------------------------------------------------- + await step.sms('send-sms', async (controls) => { + return { + subject: controls.smsSubject, + body: `Your verification code from Slack is ${payload.validationCode}`, + }; + }, + { + controlSchema: zodSmsControlSchema + }); + }, + { + payloadSchema: zodPayloadSchema + }, +); +``` diff --git a/guides/workflows/password-reset.mdx b/guides/workflows/password-reset.mdx new file mode 100644 index 00000000..53edd773 --- /dev/null +++ b/guides/workflows/password-reset.mdx @@ -0,0 +1,48 @@ +--- +title: "Password Reset" +description: "Help users reclaim access to their account via the 'Password Reset' or 'Forgot Username' functions" +--- + +## Intro + +To help users re-establish access to their account securely, a password reset email should be succint and have a unique password reset URL that expires after a given time. This example uses the payload schema to fetch the unique expiring password reset link. + +[Explore the source code on GitHub](https://github.com/novuhq/examples/tree/main/novu-workflows/react/password-reset) + +## Preview + + + + +## Code Example + +```javascript +import { workflow } from '@novu/framework'; +import { renderPasswordResetEmail } from '../emails/password-reset'; +import { zodControlSchema, zodPayloadSchema } from './schemas'; + +export const DropboxPasswordResetEmail = workflow( + "Dropbox Password Reset", + async ({ step, payload }) => { + await step.email( + "send-email", + async (controls) => { + return { + subject: controls.emailSubject, + body: renderPasswordResetEmail(controls, payload), + }; + }, + { + controlSchema: zodControlSchema + } + ); + }, + { + payloadSchema: zodPayloadSchema + } +); +``` + diff --git a/guides/workflows/recent-login.mdx b/guides/workflows/recent-login.mdx new file mode 100644 index 00000000..fd245c08 --- /dev/null +++ b/guides/workflows/recent-login.mdx @@ -0,0 +1,66 @@ +--- +title: "Recent Login" +description: "Notify users of recent login activity" +--- + +## Intro + +Sending recent logins is a great way to inform users of possible unusal activity and verify their usage. This workflow example leverages the payload schema to display the login IP, and notifies users through both email and a push notification. + +[Explore the source code on GitHub](https://github.com/novuhq/examples/tree/main/novu-workflows/react/recent-login) + +## Preview + + + + +## Code Example + +```javascript +import { workflow } from '@novu/framework'; +import { renderLoginEmail } from '../emails/yelp-recent-login'; +import { zodControlSchema, zodPayloadSchema, zodPushControlSchema } from './schemas'; + +export const YelpRecentLogin = workflow( + "Yelp Recent Login", + async ({ step, payload }) => { + /** + * Email Flow + */ + await step.email( + "send-email", + async (controls) => { + return { + subject: "Recent login to your Account", + body: renderLoginEmail(controls, payload), + }; + }, + { + controlSchema: zodControlSchema + }, + ); + + /** + * Push Flow + */ + await step.push( + "send-push", + async (controls) => { + return { + subject: controls.pushNotificationSubject, + body: `Hi ${payload.userFirstName}, we noticed a recent login to your Yelp account. If this was you, there's nothing else you need to do. If this wasn't you or please see our support page.`, + }; + }, + { + controlSchema: zodPushControlSchema + } + ); + }, + { + payloadSchema: zodPayloadSchema + }, +); +``` diff --git a/guides/workflows/scaffold.mdx b/guides/workflows/scaffold.mdx new file mode 100644 index 00000000..bc6da167 --- /dev/null +++ b/guides/workflows/scaffold.mdx @@ -0,0 +1,129 @@ +--- +title: "" +description: "" +--- + +## Intro + + +[Explore the source code on GitHub](https://github.com/novuhq/examples/tree/main/novu-workflows/react/otp) + +## Preview + + + + + +## Code Example + +```javascript +import { Client, workflow } from "@novu/framework"; +import { renderOtpEmail } from "./emails/slack-otp"; + + +export const client = new Client({ + /** + * Enable this flag only during local development + */ + strictAuthentication: false, +}); + +export const SlackVerificationOTP = workflow( + "Slack Verify OTP", + async ({ step, payload }) => { + await step.email( + "send-email", + async (inputs) => { + return { + subject: inputs.emailSubject, + body: renderOtpEmail(inputs, payload), + }; + }, + { + inputSchema: { + type: "object", + properties: { + emailSubject: { + title: "Email Subject", + type: "string", + default: "Verify your Slack OTP!" + }, + confirmAddressHeader: { + type: "string", + default: "Confirm your email address", + title: "Confirm Address Header" + }, + majorBodyText: { + type: "string", + default: "Your confirmation code is below - enter it in your open browser window and we'll help you get signed in.", + title: "Major Body Text" + }, + showMagicLink: { + type: "boolean", + default: false, + title: "Show Magic Link" + }, + linkText: { + type: 'string', + default: 'Click this link if the OTP does not work for you!', + title: 'Magic link Text' + }, + showOTP: { + type: "boolean", + default: true, + title: "Show OTP" + }, + }, + }, + }); + + // -----------------------------------push flow------------------------------------------------------------------------- + await step.push('send-push', async () => { + return { + subject: 'You received a Slack OTP', + body: `Your verification code from Slack is ${payload.validationCode}`, + }; + }); + + // -----------------------------------sms flow------------------------------------------------------------------------- + await step.sms('send-sms', async () => { + return { + subject: 'You received a Slack OTP', + body: `Your verification code from Slack is ${payload.validationCode}`, + }; + }); + }, + { + payloadSchema: { + type: "object", + properties: { + validationCode: { + type: "string", + default: "123456", + title: "OTP", + }, + magicLinkURL: { + type: "string", + default: "https://slack.com/magic/link", + title: "Magic Link URL" + } + }, + }, + }, +); + +``` + +## Installation + +``` +npm run dev +``` + + +  If you're ready to start integrating in your .NET app, jump straight to + our [.NET quickstart.](/quickstarts/.NET) + diff --git a/guides/workflows/shipping-confirmation.mdx b/guides/workflows/shipping-confirmation.mdx new file mode 100644 index 00000000..be8ab583 --- /dev/null +++ b/guides/workflows/shipping-confirmation.mdx @@ -0,0 +1,51 @@ +--- +title: "Shipping Confirmation" +description: "Let buyers know their order is on the way!" +--- + +## Intro + +What delights the dopamine receptors more than an email confirming their order's delivery date? Reward your buyers by letting them know their order is confirmed and on the way, complete with tracking info fetched by leveraging the payload schema. + +[Explore the source code on GitHub](https://github.com/novuhq/examples/tree/main/novu-workflows/react/shipping-order-confirmation) + +## Preview + + + + +## Code Example + +```javascript +import { workflow } from '@novu/framework'; +import { renderShippingOrderEmail } from '../emails/shipping-order-confirmation'; +import { zodControlSchema, zodPayloadSchema, jsonControlSchema, jsonPayloadSchema } from './schemas'; + +export const AmazonShippingOrderConfirmation = workflow( + "Amazon Shipping Order", + async ({ step, payload }) => { + await step.email( + "send-email", + async (controls) => { + return { + subject: controls.emailSubject, + body: renderShippingOrderEmail(controls, payload), + }; + }, + { + controlSchema: zodControlSchema + } + ); + }, + { + payloadSchema: zodPayloadSchema + }, +); +``` + + +  PROTIP: Delight users by sending their tracking info - leverage the payload schema to do this. + diff --git a/help/channels/chat.mdx b/help/channels/chat.mdx index 5de7eac6..2e16defb 100644 --- a/help/channels/chat.mdx +++ b/help/channels/chat.mdx @@ -7,6 +7,7 @@ icon: "comment" import ContactSupportSnippet from "/snippets/contact-support.mdx"; ### Can I send a message to a specific user on Slack? + While installing the Slack app, you will be asked the User or Channel to which the message needs to be sent. On selecting a user, a webhookUrl for that user will be generated and Novu will use that webhookUrl to send a message to that specific user. - \ No newline at end of file + diff --git a/help/channels/email.mdx b/help/channels/email.mdx index 4ec4f89c..d032ce48 100644 --- a/help/channels/email.mdx +++ b/help/channels/email.mdx @@ -7,31 +7,39 @@ icon: "envelope" import ContactSupportSnippet from "/snippets/contact-support.mdx"; ### How to override default values of Email? + All values (from, to, subject, body, etc.) can be overridden by using the `overrides` option while triggering the workflow. Please see our [email overrides documentation](/channels-and-providers/email/overview#sending-email-overrides) for more details. ### Sending HTML as the value of a variable + HTML content as a value of step variable can be sent by replacing the double curly braces `{{}}` variable with triple curly braces `{{{}}}` variable. For example, `{{{htmlContent}}}`. ### I have multiple Email Providers or integrations active, how do I associate them to a Workflow? + There can be more than one active email integrations of same or different providers. However only one email integration can be primary at one time per environment. To use a different email integration than your primary integration, the integrationIdentifier field of email overrides can be used. Read about using [different email integration](/channels-and-providers/email/overview#using-different-email-integration) for more details. ### Why am I getting the error “message content could not be generated”? + This error occurs when the HTML content is not properly formatted. Make sure the HTML content is properly formatted and does not contain any syntax errors. Make sure Step Variables are properly formatted and are not missing opening or closing curly braces. ### Can email or phone be used as step variables? + Novu recommends not to use subscriber attributes as step variables. Instead, use the subscriber attributes directly in the step. For example, use `{{subscriber.email}}` instead of `{{email}}` as your step variable. Novu will select the email from subscriber attributes and inject the value in the content. ### How to use Provider templates in place of email content? + Currently, we support external Provider template support for [sendgrid](/channels-and-providers/email/sendgrid#using-sendgrid-template) and [mailersend](/channels-and-providers/email/mailersend#using-mailersend-template) only. ### Can I retrieve only Email content? + Novu does not have a separate API to retrieve email content. However, email content can be retrieved from the email step of the workflow. The email step contains the email content in the `content` field of [get worklfow api](/api-reference/workflows/get-workflow) response. ### How to send inline images in email? + Inline images can be sent using `` tag in the email content. PNG, JPEG, GIF are supported. SVGs are not supported as inline images by most of email clients like gmail, outlook. ### How to add unsubscribe link in emails? Add `{{unsubscribe}}` variable in email step content editor and send url value of this variable in payload. Create a simple html page to [fetch preferences](https://docs.novu.co/api-reference/subscribers/get-subscriber-preferences) and then use [update preference api](https://docs.novu.co/api-reference/subscribers/update-subscriber-preference) to update. - \ No newline at end of file + diff --git a/help/channels/in-app.mdx b/help/channels/in-app.mdx index e779ade4..f286118f 100644 --- a/help/channels/in-app.mdx +++ b/help/channels/in-app.mdx @@ -7,19 +7,23 @@ icon: "bell" import ContactSupportSnippet from "/snippets/contact-support.mdx"; ### Creating two In-App Notification Centers in one account + To configure your In-App notifications, you must use the In-App integration from our integration store. -To display in-app notifications to users, any SDKs in our [client library](/notification-center/introduction) can be used. Every library requires `applicationIdentifier` and `subscriberId` to fetch notifications. Currently, only one `applicationIdentifier` is supported per environment per organization. +To display in-app notifications to users, any SDKs in our [client library](/notification-center/introduction) can be used. Every library requires `applicationIdentifier` and `subscriberId` to fetch notifications. Currently, only one `applicationIdentifier` is supported per environment per organization. Feel free to reach out to us at support@novu.co if you have speicific requirements of multiple `applicationIdentifier` per organization. ### Issues with a redirect URL + To redirect to specified redirect url on clicking the in-app notification, notification click event should be handled in client side code. Here is an example in [React](/notification-center/client/react/get-started#onnotificationclick). ### Changing the default colors of the Notification Center -Styling of your In-App Notification Center can be customized using the [styles](/notification-center/client/react/customization#customization-using-styles-prop) prop. If you want to customize only a single component, you can use [available](/notification-center/client/react/customization#popovernotificationcenter-customization) props. + +Styling of your Inbox can be customized using the [styles](/notification-center/client/react/customization#customization-using-styles-prop) prop. If you want to customize only a single component, you can use [available](/notification-center/client/react/customization#popovernotificationcenter-customization) props. ### Additional Security for In-App Notifications + To add an extra layer of security, we recommend using our HMAC encryption feature to eliminate subscriber impersonation Checkout [HMAC](/notification-center/client/react/get-started#hmac-encryption) section for more details. - \ No newline at end of file + diff --git a/help/channels/push.mdx b/help/channels/push.mdx index d128b709..a024a4d3 100644 --- a/help/channels/push.mdx +++ b/help/channels/push.mdx @@ -7,17 +7,22 @@ icon: "mobile" import ContactSupportSnippet from "/snippets/contact-support.mdx"; ### Can I send a Desktop Push notification? + Yes, Desktop Push Notifications can be sent by using a Web Push Notification. It is supported with almost all [push channel](/channels-and-providers/push/overview) providers. ### Why am I getting the error “unexpected provider” with FCM? + The possible reasons for this error are: + - Invalid FCM credentials. - Invalid or stale FCM token. ### Why am I getting the error “Sending message failed due to Requested entity was not found” with FCM? + - This error occurs when the FCM token is invalid or stale. You can fix this by updating the FCM device token. ### Can I store device tokens for particular FCM integration if I have multiple FCM providers active? + - Device tokens for particular FCM integration can be stored using the `integrationIdentifier` field of [update subscriber credentials api](/api-reference/subscribers/update-subscriber-credentials). `integrationIdentifier` is the unique identifier of the integration which is similar to `providerIdentifier`. It can be copied from the integration form. - \ No newline at end of file + diff --git a/help/channels/sms.mdx b/help/channels/sms.mdx index e83565e3..e29b0146 100644 --- a/help/channels/sms.mdx +++ b/help/channels/sms.mdx @@ -7,12 +7,13 @@ icon: "message-sms" import ContactSupportSnippet from "/snippets/contact-support.mdx"; ### Why am I getting the error “ subscriber does not have a configured channel”? + This error occurs when a Subscriber is missing `phone` attribute. To fix this error, you need to update the subscriber and set the `phone` attribute with a valid phone number. ### How to change the default sender name for SMS? -First, ensure a custom sendername (sender id or from) is allowed with your Provider. Some countries expect to verify the sender name before using it. -You can change the default sender name for SMS by using [overrides](/channels-and-providers/sms/overview#sending-sms-overrides) field while triggering the workflow. +First, ensure a custom sendername (sender id or from) is allowed with your Provider. Some countries expect to verify the sender name before using it. +You can change the default sender name for SMS by using [overrides](/channels-and-providers/sms/overview#sending-sms-overrides) field while triggering the workflow. - \ No newline at end of file + diff --git a/help/content-management.mdx b/help/content-management.mdx index f4b34e78..7831c123 100644 --- a/help/content-management.mdx +++ b/help/content-management.mdx @@ -7,18 +7,23 @@ icon: "file-pen" import ContactSupportSnippet from "/snippets/contact-support.mdx"; ### What is a Layout and how can I use them? + Layout is similar to a style container for email templates. Layouts can be used for your organization’s branding and style. It can be used to define the structure of the email template such as the header, footer, and other common elements. It must have a `{{{body}}}` variable which will be replaced by the actual email template content. ### Can Layouts be used across all channels? + Layouts are specific to Emails. ### Can MJML templates be used in content editors? + Only HTML templates can be used in content editors. We support [handlebar helpers](/content-creation-design/handlebars-helpers) for common use cases like iteration, conditions etc. If you need any specific Handlebar Helpers, please reach out to us at support@novu.co. ### Can the content of one Step be used in another? + Currently each Step has content that is unique to it and can not be reused. If this is important to you, please send us a note at support@novu.co. ### Which Translation Language will be used if the Subscriber’s locale field does not match with translation language files? -If the Subscriber's locale field does not match with Translation locale files, or it is not available, then the defaul language of the translation group will be used. If a default is not set, the automatic fallback is English. - \ No newline at end of file +If the Subscriber's locale field does not match with Translation locale files, or it is not available, then the defaul language of the translation group will be used. If a default is not set, the automatic fallback is English. + + diff --git a/help/observability.mdx b/help/observability.mdx index 5bdf6b84..527cde9a 100644 --- a/help/observability.mdx +++ b/help/observability.mdx @@ -7,13 +7,15 @@ description: "Analyzing your Triggered Events and Notification execution logs" import ContactSupportSnippet from "/snippets/contact-support.mdx"; ### My Subscribers have preferences set, but the Activity Feed is showing Triggers as being sent. + The Activity Feed is a place each event (Workflow Trigger) is listed. If a Subscriber has a preference to not receive notifications through a certain channel, Novu’s system will adhere to that. However, this is still a valid Trigger as the fanout occurs before preferences are accommodated. ### What does "step filtered by subscriber preferences" mean in the logs? + It means that the Subscriber has set preferences configured and the step was filtered based on that preference. If the subscriber has disabled a step Channel then, the step will be filtered and notification will not be sent to the subscriber. ### How to export Activity Feed data? -Currently, we don't have any option to export activity feed data. But you can use our [API](/api-reference/notification/get-notifications) to fetch the data. If there is a specific system you’re looking to integrate with, please send us a note at support@novu.co. +Currently, we don't have any option to export activity feed data. But you can use our [API](/api-reference/notification/get-notifications) to fetch the data. If there is a specific system you’re looking to integrate with, please send us a note at support@novu.co. - \ No newline at end of file + diff --git a/help/orchestration.mdx b/help/orchestration.mdx index 9766af68..db562b72 100644 --- a/help/orchestration.mdx +++ b/help/orchestration.mdx @@ -7,21 +7,25 @@ import ContactSupportSnippet from "/snippets/contact-support.mdx"; ## Digest ### New event triggers are merging an active Digest with an old duration.. -When a Digest is created with a set duration and an event is triggered, the Digest stays active ( and will batch the events) until the configured duration is over. -If you change the duration before the initial time period has completed , the previous digest will still be active and the new duration is merged with the old duration Digest until the previous duration is complete. +When a Digest is created with a set duration and an event is triggered, the Digest stays active ( and will batch the events) until the configured duration is over. + +If you change the duration before the initial time period has completed , the previous digest will still be active and the new duration is merged with the old duration Digest until the previous duration is complete. To remove the previous Digest, use [event cancel api](/api-reference/events/cancel-triggered-event) to cancel all event triggers that are still pending and are merged in active Digests. ### Can I dynamically make the Digest inactive?? + Yes, Digest supports conditions and can be applied to your Digest step. If the Triggered Event passes the set conditions, then it will be active or inactive based on the condition. ### Can a Subscriber control the Digest duration and time? -Not currently. The Digest duration can be changed at the Workflow level only and is applied to all subscribers that Workflow is triggered to. If you have an identified use case for this, please let us know at support@novu.co. + +Not currently. The Digest duration can be changed at the Workflow level only and is applied to all subscribers that Workflow is triggered to. If you have an identified use case for this, please let us know at support@novu.co. ## Delay ### What happens if the delay scheduled date and time is in the past? + If the delay scheduled date and time is in the past, the delay step will fail. Based on the step configuration, workflow will either continue to the next step or workflow will be failed - \ No newline at end of file + diff --git a/help/others.mdx b/help/others.mdx index f209bbea..fb05f60f 100644 --- a/help/others.mdx +++ b/help/others.mdx @@ -3,7 +3,9 @@ icon: "crystal-ball" --- ### Workkflows changes are not showing on the changes + If some changes are pending to promote for the same workflow, then Novu merges subsequent changes into the existing change item. ### Getting CORS error with the API -Novu APIs are developed to be used on the server side. If you are using the API from the browser, you will get a CORS error. You can use the API from the server side or checkout our [client-side libraries](/notification-center/introduction) to fetch in-app notifications. \ No newline at end of file + +Novu APIs are developed to be used on the server side. If you are using the API from the browser, you will get a CORS error. You can use the API from the server side or checkout our [client-side libraries](/notification-center/introduction) to fetch in-app notifications. diff --git a/help/overview.mdx b/help/overview.mdx index 6b07179f..819d5d52 100644 --- a/help/overview.mdx +++ b/help/overview.mdx @@ -8,7 +8,12 @@ icon: "bolt" At our Help Center, we're dedicated to provide comprehensive support to ensure your experience with our products and services is seamless and enjoyable. Whether you're a new user looking for guidance or a seasoned customer seeking assistance, we're here to help. Help Center is organized as per our product architecture and is divided into the following sections: - + Manage your account and access your data. - Explore orchestration digest, delay, conditions and more. + Explore orchestration digest, delay, conditions and more. - Content management, layouts, templates, and more. + Content management, layouts, templates, and more. - Observability, metrics, analytics, and more. + Observability, metrics, analytics, and more. - - Others help topics. + + Others help topics. Read frequently asked questions - - \ No newline at end of file + + + diff --git a/help/user-management.mdx b/help/user-management.mdx index a1544392..f767c4fa 100644 --- a/help/user-management.mdx +++ b/help/user-management.mdx @@ -7,31 +7,39 @@ icon: "users" ## Subscribers ### What is a Subscriber? + A subscriber is an entity to which notifications are sent. It is a Novu term similar to a user in your system. As in your system, a user is identified by a unique id, similarly in Novu, a subscriber is identified by a unique id called `subscriberId`. We recommend that the existing value you use in your system as a `userId` to be used as `subscriberId` in Novu. Read more about subscribers on [subscribers documentation page](/subscribers/subscribers). ### Is it necessary to use subscriberId as same as userId? + No, it is not necessary to use subscriberId as same as userId. You can use any unique id as subscriberId. We recommend using userId as subscriberId to avoid any confusion. Some of our customers use a pattern like `auth0|userId` as a value for `subscriberId`. ### Can notification be sent without adding a Subscriber? + No, a notification cannot be sent without adding a subscriber. A subscriber is an entity to which notifications are sent. You need to add a subscriber to Novu before triggering the workflow or inline of trigger. Read more about subscribers on [subscribers documentation page](/subscribers/subscribers). ### What is the right approach to add Subscribers? -Subscribers can be added either [ahead of trigger](/subscribers/subscribers#1-ahead-of-trigger) or [inline of trigger](/subscribers/subscribers#2-inline-of-trigger). One way to add a subscriber using inline of trigger is to create a new subscriber in Novu as soon as they sign up or register in your system. + +Subscribers can be added either [ahead of trigger](/subscribers/subscribers#1-ahead-of-trigger) or [inline of trigger](/subscribers/subscribers#2-inline-of-trigger). One way to add a subscriber using inline of trigger is to create a new subscriber in Novu as soon as they sign up or register in your system. You can add your existing users as subscribers in Novu using [bulk create subscriber creation method](/subscribers/subscribers#bulk-subscriber-creation). ### Can Subscriber credentials be added while creating a new Subscriber? -Subscriber credentials for Push and Chat channel providers cannot be added while creating a new subscriber. Subscriber credentials for Push and Chat channel providers can be added using the [update subscriber credentials api](/api-reference/subscribers/update-subscriber-credentials) or corresponding sdk methods. + +Subscriber credentials for Push and Chat channel providers cannot be added while creating a new subscriber. Subscriber credentials for Push and Chat channel providers can be added using the [update subscriber credentials api](/api-reference/subscribers/update-subscriber-credentials) or corresponding sdk methods. SMS and Email channels are a bit different because all providers can work with the same value, so `email` and `phone` [attributes](/subscribers/subscribers#subscriber-attributes) can be added while creating a new Subscriber. ### Can Subscriber preferences be updated while adding a new Subscriber? + Subscriber preference cannot be updated while adding a new subscriber. Novu sets all channels as true by default for new subscribers. You can modify preferences at any time. Read more about [subscriber preferences](/subscribers/preferences). ### Can two subscribers with different `subscriberId`s have the same email address? + Yes, it is possible for two subscribers to have the same email address in our system. This is because the `subscriberId` is the unique identifier that distinguishes one subscriber from another. Even if two subscribers share the same email address, they will have different `subscriberId`s, which allows our system to identify and manage them separately. ## Topics ### Can a Subscriber be added to multiple topics at once? -Currently, Novu supports adding a subscriber to only one topic at a time. You can add a subscriber to multiple topics by calling the [add subscriber to topic api](/api-reference/topics/subscribers-addition) multiple times with a particular topic key \ No newline at end of file + +Currently, Novu supports adding a subscriber to only one topic at a time. You can add a subscriber to multiple topics by calling the [add subscriber to topic api](/api-reference/topics/subscribers-addition) multiple times with a particular topic key diff --git a/images/how-to/feedback-review.png b/images/how-to/feedback-review.png new file mode 100644 index 00000000..a438114a Binary files /dev/null and b/images/how-to/feedback-review.png differ diff --git a/images/how-to/invoice-receipt.png b/images/how-to/invoice-receipt.png new file mode 100644 index 00000000..dfc55725 Binary files /dev/null and b/images/how-to/invoice-receipt.png differ diff --git a/images/how-to/otp-email.png b/images/how-to/otp-email.png new file mode 100644 index 00000000..40160da7 Binary files /dev/null and b/images/how-to/otp-email.png differ diff --git a/images/how-to/otp-push.png b/images/how-to/otp-push.png new file mode 100644 index 00000000..10432873 Binary files /dev/null and b/images/how-to/otp-push.png differ diff --git a/images/how-to/otp-sms.png b/images/how-to/otp-sms.png new file mode 100644 index 00000000..f29131b3 Binary files /dev/null and b/images/how-to/otp-sms.png differ diff --git a/images/how-to/otp.png b/images/how-to/otp.png new file mode 100644 index 00000000..fb33c715 Binary files /dev/null and b/images/how-to/otp.png differ diff --git a/images/how-to/password-reset.png b/images/how-to/password-reset.png new file mode 100644 index 00000000..e67ee34d Binary files /dev/null and b/images/how-to/password-reset.png differ diff --git a/images/how-to/recent-login-email.png b/images/how-to/recent-login-email.png new file mode 100644 index 00000000..3a60f6bf Binary files /dev/null and b/images/how-to/recent-login-email.png differ diff --git a/images/how-to/recent-login-push.png b/images/how-to/recent-login-push.png new file mode 100644 index 00000000..5d914d64 Binary files /dev/null and b/images/how-to/recent-login-push.png differ diff --git a/images/how-to/recent-login.png b/images/how-to/recent-login.png new file mode 100644 index 00000000..9f722a79 Binary files /dev/null and b/images/how-to/recent-login.png differ diff --git a/images/how-to/shipping-confirmation.png b/images/how-to/shipping-confirmation.png new file mode 100644 index 00000000..68e4b1ab Binary files /dev/null and b/images/how-to/shipping-confirmation.png differ diff --git a/images/how-to/shipping-order-confirmation.png b/images/how-to/shipping-order-confirmation.png new file mode 100644 index 00000000..fb6a39ed Binary files /dev/null and b/images/how-to/shipping-order-confirmation.png differ diff --git a/notification-center/backend/methods.mdx b/inbox/backend/methods.mdx similarity index 100% rename from notification-center/backend/methods.mdx rename to inbox/backend/methods.mdx diff --git a/notification-center/client/angular.mdx b/inbox/client/angular.mdx similarity index 87% rename from notification-center/client/angular.mdx rename to inbox/client/angular.mdx index ae763798..dcbcc9da 100644 --- a/notification-center/client/angular.mdx +++ b/inbox/client/angular.mdx @@ -11,15 +11,9 @@ The `@novu/notification-center-angular` package provides an Angular component Novu supports Angular version 15 and higher. -```bash pnpm -pnpm i @novu/notification-center-angular -``` -```bash npm -npm i @novu/notification-center-angular -``` -```bash yarn -yarn add @novu/notification-center-angular -``` + ```bash pnpm pnpm i @novu/notification-center-angular ``` ```bash npm npm i + @novu/notification-center-angular ``` ```bash yarn yarn add + @novu/notification-center-angular ``` ## Example usage @@ -71,7 +65,8 @@ Component HTML template: The `notification-center-component` accepts the same set of props as the [web component](/notification-center/client/web-component). - You may need to add `"allowSyntheticDefaultImports": true` in tsconfig.json for the web component's React imports to work correctly. + You may need to add `"allowSyntheticDefaultImports": true` in tsconfig.json + for the web component's React imports to work correctly. ## Example diff --git a/notification-center/client/iframe.mdx b/inbox/client/iframe.mdx similarity index 97% rename from notification-center/client/iframe.mdx rename to inbox/client/iframe.mdx index aa172f78..17770824 100644 --- a/notification-center/client/iframe.mdx +++ b/inbox/client/iframe.mdx @@ -198,7 +198,7 @@ Then pass the created HMAC to your client side application forward it to the emb - We use font awesome bell icon. Make sure you have added the font awesome CSS cdn link in the head tag. +We use font awesome bell icon. Make sure you have added the font awesome CSS cdn link in the head tag. ```html ``` - + - \ No newline at end of file + diff --git a/notification-center/client/vue.mdx b/inbox/client/vue.mdx similarity index 73% rename from notification-center/client/vue.mdx rename to inbox/client/vue.mdx index 317a5810..14bb6965 100644 --- a/notification-center/client/vue.mdx +++ b/inbox/client/vue.mdx @@ -92,19 +92,16 @@ You can pass the custom bell button component as the default slot to the `Notif ``` ## Example - + - [Vue Notification Center Example](https://github.com/novuhq/examples/tree/main/vue-notification-center-example) - [CodeSandBox Link 🔗](https://codesandbox.io/p/sandbox/novu-vue-in-app-88p8vx) - ## Props The `NotificationCenterComponent` accepts the same set of props as the [Web Component](./web-component#properties). It also accepts an extra prop - -| Prop | Type | Description | -| ----------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| triggers | string[] | This is an Array of events that controls the underlying popper component's trigger. [Read more here](https://floating-vue.starpad.dev/api/#triggers) - +| Prop | Type | Description | +| -------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | +| triggers | string[] | This is an Array of events that controls the underlying popper component's trigger. [Read more here](https://floating-vue.starpad.dev/api/#triggers) | diff --git a/notification-center/client/web-component.mdx b/inbox/client/web-component.mdx similarity index 99% rename from notification-center/client/web-component.mdx rename to inbox/client/web-component.mdx index 7d648f69..fe7a364e 100644 --- a/notification-center/client/web-component.mdx +++ b/inbox/client/web-component.mdx @@ -90,7 +90,7 @@ If you are using TypeScript, then you might want to check the `NotificationCent | socketUrl | string | Custom WebSocket Service location in case of self-hosted version of Novu. | | subscriberId | string | The subscriberId is a custom identifier used when identifying your users within the Novu platform. | | applicationIdentifier | string | Is a public identifier used to identify your application, can be found in the Novu Admin panel. | -| subscriberHash | string | The hashed subscriber id. [Read more about hmac encryption](/notification-center/client/react/get-started#hmac-encryption). | +| subscriberHash | string | The hashed subscriber id. [Read more about hmac encryption](/notification-center/client/react/get-started#hmac-encryption). | | stores | object[] | The array of the store items that are bound with the feed, it's used together with tabs property if you would like to have separate feeds in the notification center. [Read more here](/notification-center/client/react/multiple-tabs) | | | | tabs | object[] | The array of items that do describe the tabs/feeds, they are bound with the stores property. [Read more here](/notification-center/client/react/multiple-tabs). | @@ -101,7 +101,7 @@ If you are using TypeScript, then you might want to check the `NotificationCent | theme | [INovuTheme](/notification-center/client/react/api-reference#the-theme-interface) | The object defining the the light/dark styles of the Notification Center component. We discourage you to use this prop to do the styling, instead it's recommended to use the styles property. | | styles | [NotificationCenterStyles](/notification-center/client/react/api-reference#styles-interface) | The object allows you to define the custom CSS code. | | colorScheme | string | The prop defines which color version of the styles the component should set. The options are light and dark. | -| i18n | object | The object allowing to [customize the UI language](/notification-center/client/react/customization#customize-the-ui-language). | +| i18n | object | The object allowing to [customize the UI language](/notification-center/client/react/customization#customize-the-ui-language). | | onLoad | function | The callback function that is called when the session is successfully initiated. It receives the first argument object with the organization property. There is a sibling property sessionLoaded that might be used in environments that do not allow to use properties with the prefix on, like in Angular. | | onNotificationClick | function | The callback function that is called when the user has clicked on the notification. The first argument it receives is the notification object. There is a sibling property notificationClicked. | | onUnseenCountChanged | function | The callback function that is called when the unseen notifications count changes. The first argument it receives is the number of unseen notifications count. There is a sibling property unseenCountChanged. | diff --git a/notification-center/client/headless/api-reference.mdx b/inbox/headless/api-reference.mdx similarity index 98% rename from notification-center/client/headless/api-reference.mdx rename to inbox/headless/api-reference.mdx index 9ad99f73..c4b63d9b 100644 --- a/notification-center/client/headless/api-reference.mdx +++ b/inbox/headless/api-reference.mdx @@ -195,15 +195,15 @@ headlessService.fetchNotifications({ ); }, page: pageNumber, - query: { - feedIdentifier: "feedId", - read: false, - seen: true, + query: { + feedIdentifier: "feedId", + read: false, + seen: true, // payload sent while triggering the workflow payload: { - message : "Notification message", + message: "Notification message", type: "product", - } + }, }, storeId: "storeId", }); @@ -399,7 +399,6 @@ interface IMarkNotificationsAs { } ``` - ## removeNotification Removes a single notification using the message id. @@ -442,9 +441,7 @@ Removes multiple notifications using array of message ids (limit of 100). ```typescript headlessService.removeNotifications({ - listener: ( - result: UpdateResult - ) => { + listener: (result: UpdateResult) => { console.log(result); }, onSuccess: (message: IMessage) => { @@ -453,7 +450,7 @@ headlessService.removeNotifications({ onError: (error: unknown) => { console.error(error); }, - messageIds: ["message_id_1", "message_id_2" ], + messageIds: ["message_id_1", "message_id_2"], }); ``` diff --git a/notification-center/client/headless/get-started.mdx b/inbox/headless/get-started.mdx similarity index 88% rename from notification-center/client/headless/get-started.mdx rename to inbox/headless/get-started.mdx index a7d8010b..15125461 100644 --- a/notification-center/client/headless/get-started.mdx +++ b/inbox/headless/get-started.mdx @@ -85,7 +85,10 @@ headlessService.fetchNotifications({ }); ``` -fetchNotifications and other methods of the headless service should be used after initializing the session using headlessService.initializeSession + + fetchNotifications and other methods of the headless service should be used + after initializing the session using headlessService.initializeSession + ## HMAC Encryption @@ -126,7 +129,8 @@ headlessService.listenUnreadCountChange({ ``` -If you're ready to start using the Headless Notification Center, check out our [guide!](/guides/headless-notification-center-guide) + If you're ready to start using the Headless Notification Center, check out our + [guide!](/guides/headless-notification-center-guide) [Explore Implementation of the Headless Library in an App](https://github.com/novuhq/novu-headless-demo-app) @@ -139,11 +143,12 @@ If you're ready to start using the Headless Notification Center, check out our [ We have `headless.initializeSession` method to generate token and initialize the session. If user log out and new user login, then calling again this method with second user subscriberId will generate new token and new session. + - Currently, headless can not be used with react native and flutter. Checkout the progress on mobile platform support on github issue [#4499](https://github.com/novuhq/novu/issues/4499). - +Currently, headless can not be used with react native and flutter. Checkout the progress on mobile platform support on github issue [#4499](https://github.com/novuhq/novu/issues/4499). + diff --git a/notification-center/introduction.mdx b/inbox/introduction.mdx similarity index 69% rename from notification-center/introduction.mdx rename to inbox/introduction.mdx index a4ce4565..8c41a944 100644 --- a/notification-center/introduction.mdx +++ b/inbox/introduction.mdx @@ -1,24 +1,29 @@ --- -title: "Introduction to Notification Center" -description: "Learn more about Novu's Notification Center." -icon: "house" +title: "Introduction" +description: "Learn more about Novu's Inbox Component." --- -The notification center is a component, also sometimes referred to as a widget that furnishes your web application with a beautiful and smooth In-App notification experience for users. +The Inbox component, also sometimes referred to as a widget that furnishes your web application with a beautiful and smooth Inbox notification experience for users. -In-App notifications are different from push notifications in the sense that In-App notifications are consumed by the user in your app itself, whether push notifications are shown on the device level. Read more about [push channel and supported providers.](/channels-and-providers/push/overview) +In-App notifications are different from push notifications in the sense that Inbox notifications are consumed by the user in your app itself, whether push notifications are shown on the device level. Read more about [push channel and supported providers.](/channels-and-providers/push/overview) Novu provides a pre-built, ready-to-use, and customizable UI component. It also allows you to bring your own UI via a headless component which can be used to add In-App notifications to any platform. - + ## Example [![Edit kind-nova-xv6s9g](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/kind-nova-xv6s9g?fontsize=14&hidenavigation=1&theme=dark) -## Why use the Novu notification center? +## Why use the Novu Inbox? -- Users can manage subscriber preferences from the notification center itself. +- Users can manage subscriber preferences from the Invox component. - You want to notify about any product activity related to the user. - Prebuilt UI component so no need to build UI from scratch. - Highly customizable as per your product design system. diff --git a/notification-center/client/react/api-reference.mdx b/inbox/react/api-reference.mdx similarity index 90% rename from notification-center/client/react/api-reference.mdx rename to inbox/react/api-reference.mdx index d066ccf9..0bd532ed 100644 --- a/notification-center/client/react/api-reference.mdx +++ b/inbox/react/api-reference.mdx @@ -22,10 +22,10 @@ It's responsible for establishing the notification center session, managing the | socketUrl | string (optional) | The custom socket URL, your own deployed instance of the WS app. The default is https://ws.novu.co/. | | applicationIdentifier | string | The app id is taken from the Novu Settings -> API Keys tab. | | subscriberId | string | The unique user id from your system. | -| subscriberHash | string (optional) | The HMAC encrypted subscriberId. Read about [hmac encryption](/notification-center/client/react/get-started#hmac-encryption) | +| subscriberHash | string (optional) | The HMAC encrypted subscriberId. Read about [hmac encryption](/notification-center/client/react/get-started#hmac-encryption) | | stores | [IStore](#store-interface) (optional) | The connection object between feed and notification center tab. | | i18n | [ITranslationEntry \| ITranslationContent](#i18nlanguage-interface) (optional) | The prop allows to configure the UI language. | -| styles | [NotificationCenterStyles](#styles-interface) (optional) | The prop allowing to style the notification center. It's based on the CSSInterpolation object from the [@emotion/css](https://emotion.sh/docs/@emotion/css#object-styles) package. | +| styles | [NotificationCenterStyles](#styles-interface) (optional) | The prop allowing to style the notification center. It's based on the CSSInterpolation object from the [@emotion/css](https://emotion.sh/docs/@emotion/css#object-styles) package. | | initialFetchingStrategy | [IFetchingStrategy](#fetching-strategy-interface) (optional) | The fetching strategy. By default notifications feed and user preferences are not fetched until the notification center is opened. You might need to tweak this configuration when building a custom UI and if you need to fetch notifications right away. Also it's possible to change it when some action happens using the useNovuContext hook and setFetchingStrategy prop. | | children | object | The ReactNode object type, the "consumer" of the NovuProvider context. | | onLoad | [function](#props-interface) (optional) | The function that is called with an organization object after the notification center session is initialized. | @@ -60,7 +60,7 @@ interface IStoreQuery { feedIdentifier?: string | string[]; seen?: boolean; read?: boolean; - limit?: number; + limit?: number; payload?: Record; } ``` @@ -173,6 +173,7 @@ type ObjectWithRoot = T & { root: CSSFunctionOrObject; }; ``` + To use the Styles Interface, you need to identify the property you need to modify to achieve your desired look and pass it as an object in the `styles` prop.
    @@ -200,6 +201,7 @@ Now, that we know which property needs to change, all we need to do is pass an o {({ unseenCount }) => } + ``` This gives us the desired result: @@ -376,12 +378,13 @@ interface ISvgStopColor { stopColorOffset?: string; } ``` + ## Hooks ### useNotifications This hook that allows you to get the notifications feed data and unseen notifications count. The notifications are fetched only when the `fetchingStrategy.fetchNotifications` is set to true interface, check the `initialFetchingStrategy` prop on the [NovuProvider](#novuprovider) component. - + ```typescript const result = useNotifications(); ``` @@ -413,31 +416,31 @@ interface INotificationsContext { removeAllMessages: (feedId?: string) => void; } ``` -Description of the `useNotifications` hook return interface properties -| Prop | Type | Description | -| --- | --- | --- | -| storeId | string | active `storeId` | -| stores | [IStore[]](#store-interface) | array of [IStore](#store-interface) passed to the [NovuProvider](#novuprovider). | -| unseenCount | number | unseen notifications (messages) count. | -| notifications | [IMessage[]](#the-notification-imessage-model) | The feed notifications array. | -| hasNextPage | boolean | flag indicating if the next notifications page to fetch is available. | -| isLoading | boolean | flag indicating if the initial notifications fetch request is on the fly. | -| isFetching | boolean | flag indicating if the notifications are fetching including initial fetch. | -| isFetchingNextPage | boolean | flag indicating if the next notifications page request in on the fly. | -| setStore | function | function used to change the current notifications [IStore](#store-interface) | -| fetchNextPage | function | function that fires the fetch request for the next notifications page. | -| refetch | function | function used to refetch all the notifications, when multiple page requests were made it will refetch all of them. | -| markNotificationAsRead | function | function takes the notification (message) `messageId` as an argument and marks that notification (message) as read. | -| markNotificationAsUnRead | function | function takes the notification (message) `messageId` as an argument and marks that notification (message) as unread. | -| markNotificationAsSeen | function | function takes the notification (message) `messageId` as an argument and marks that notification (message) as seen. | -| markFetchedNotificationsAsRead | function | function marks all the fetched notifications as read. | -| markFetchedNotificationsAsSeen | function | function allowing you to mark all the fetched notifications as seen. | -| markAllNotificationsAsRead | function | function allowing you to mark all notifications as read. | -| markAllNotificationsAsSeen | function | function allowing you to mark notifications as seen. | -| removeMessage | function | function takes the notification (message) `messageId` as an argument and delete a notification. | -| removeAllMessages | function | The function allowing you to delete all notifications or a specific feed's (store's) notifications at once. | +Description of the `useNotifications` hook return interface properties +| Prop | Type | Description | +| ------------------------------ | ---------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | +| storeId | string | active `storeId` | +| stores | [IStore[]](#store-interface) | array of [IStore](#store-interface) passed to the [NovuProvider](#novuprovider). | +| unseenCount | number | unseen notifications (messages) count. | +| notifications | [IMessage[]](#the-notification-imessage-model) | The feed notifications array. | +| hasNextPage | boolean | flag indicating if the next notifications page to fetch is available. | +| isLoading | boolean | flag indicating if the initial notifications fetch request is on the fly. | +| isFetching | boolean | flag indicating if the notifications are fetching including initial fetch. | +| isFetchingNextPage | boolean | flag indicating if the next notifications page request in on the fly. | +| setStore | function | function used to change the current notifications [IStore](#store-interface) | +| fetchNextPage | function | function that fires the fetch request for the next notifications page. | +| refetch | function | function used to refetch all the notifications, when multiple page requests were made it will refetch all of them. | +| markNotificationAsRead | function | function takes the notification (message) `messageId` as an argument and marks that notification (message) as read. | +| markNotificationAsUnRead | function | function takes the notification (message) `messageId` as an argument and marks that notification (message) as unread. | +| markNotificationAsSeen | function | function takes the notification (message) `messageId` as an argument and marks that notification (message) as seen. | +| markFetchedNotificationsAsRead | function | function marks all the fetched notifications as read. | +| markFetchedNotificationsAsSeen | function | function allowing you to mark all the fetched notifications as seen. | +| markAllNotificationsAsRead | function | function allowing you to mark all notifications as read. | +| markAllNotificationsAsSeen | function | function allowing you to mark notifications as seen. | +| removeMessage | function | function takes the notification (message) `messageId` as an argument and delete a notification. | +| removeAllMessages | function | The function allowing you to delete all notifications or a specific feed's (store's) notifications at once. | ### useFetchNotifications @@ -447,8 +450,8 @@ Description of the `useNotifications` hook return interface properties const query: IStoreQuery = { limit: 10, payload: { - key: "value" - } + key: "value", + }, }; const onSuccess = (data: IPaginatedResponse) => {}; @@ -474,7 +477,6 @@ interface IUseFetchNotificationsArgs { onError?: (error: Error) => void; // ... many more from the react-query useInfiniteQuery hook } - ``` `useFetchNotifications` hook return interface @@ -507,7 +509,10 @@ const onSuccess = ({ count }: ICountData) => {}; const onError = (error: Error) => {}; -const { data, isLoading, isError, error } = useUnseenCount({ onSuccess, onError }); +const { data, isLoading, isError, error } = useUnseenCount({ + onSuccess, + onError, +}); ``` `useUnseenCount` hook args interface @@ -593,7 +598,7 @@ interface ICountData { } const query: IStoreQuery = { - feedIdentifier: "product-updates" + feedIdentifier: "product-updates", }; const onSuccess = (data: ICountData) => {}; @@ -636,7 +641,7 @@ interface IOrganizationEntity { members: { _id: string; _userId?: string; - user?: Pick; + user?: Pick; roles: MemberRoleEnum[]; invite?: IMemberInvite; memberStatus: MemberStatusEnum; @@ -647,7 +652,7 @@ interface IOrganizationEntity { fontColor: string; fontFamily: string; contentBackground: string; - direction: 'ltr' | 'rtl'; + direction: "ltr" | "rtl"; }; } @@ -736,10 +741,11 @@ const onSuccess = (data: IUserPreferenceSettings) => {}; const onError = (error: Error) => {}; -const { updateUserPreferences, isLoading, isError, error } = useUpdateUserPreferences({ - onSuccess, - onError, -}); +const { updateUserPreferences, isLoading, isError, error } = + useUpdateUserPreferences({ + onSuccess, + onError, + }); ``` `useUpdateUserPreferences` hook args interface @@ -779,7 +785,10 @@ const onSuccess = (data: IMessage) => {}; const onError = (error: Error) => {}; -const { updateAction, isLoading, isError, error } = useUpdateAction({ onSuccess, onError }); +const { updateAction, isLoading, isError, error } = useUpdateAction({ + onSuccess, + onError, +}); ``` `useUpdateAction` hook args interface @@ -820,10 +829,11 @@ const onSuccess = (data: IMessage[]) => {}; const onError = (error: Error) => {}; -const { markNotificationsAs, isLoading, isError, error } = useMarkNotificationsAs({ - onSuccess, - onError, -}); +const { markNotificationsAs, isLoading, isError, error } = + useMarkNotificationsAs({ + onSuccess, + onError, + }); ``` `useMarkNotificationsAs` hook args interface @@ -863,10 +873,12 @@ const onSuccess = (data: IMessage) => {}; const onError = (error: Error) => {}; -const { removeNotification, isLoading, isError, error } = useRemoveNotification({ - onSuccess, - onError, -}); +const { removeNotification, isLoading, isError, error } = useRemoveNotification( + { + onSuccess, + onError, + } +); ``` `useRemoveNotification` hook args interface @@ -906,10 +918,11 @@ const onSuccess = (data: IMessage) => {}; const onError = (error: Error) => {}; -const { removeNotifications, isLoading, isError, error } = useRemoveNotifications({ - onSuccess, - onError, -}); +const { removeNotifications, isLoading, isError, error } = + useRemoveNotifications({ + onSuccess, + onError, + }); ``` `useRemoveNotifications` hook args interface @@ -947,10 +960,11 @@ const onSuccess = (data: IMessage) => {}; const onError = (error: Error) => {}; -const { removeAllNotifications, isLoading, isError, error } = useRemoveAllNotifications({ - onSuccess, - onError, -}); +const { removeAllNotifications, isLoading, isError, error } = + useRemoveAllNotifications({ + onSuccess, + onError, + }); ``` `useRemoveAllNotifications` hook args interface @@ -1150,11 +1164,12 @@ customization properties | subscriber | [SubscriberProps](#subscriberprops) | Subscriber object that contains information about the subscriber | ### SubscriberProps -| Property | Type | Description | -| ------------ | --------------------------------- | -------------------------------------------------------------------- | -| _id | string | Auto-generated identifier for the subscriber entity in the database | -| firstName | string | First name of the subscriber | -| subscriberId | string | identifier of the subscriber entity in the Novu Web Dashboard | + +| Property | Type | Description | +| ------------ | ------ | ------------------------------------------------------------------- | +| \_id | string | Auto-generated identifier for the subscriber entity in the database | +| firstName | string | First name of the subscriber | +| subscriberId | string | identifier of the subscriber entity in the Novu Web Dashboard | ### IMessageButton diff --git a/notification-center/client/react/customization.mdx b/inbox/react/customization.mdx similarity index 99% rename from notification-center/client/react/customization.mdx rename to inbox/react/customization.mdx index 8a43a1fb..cd8be39c 100644 --- a/notification-center/client/react/customization.mdx +++ b/inbox/react/customization.mdx @@ -241,8 +241,8 @@ export const styles = { color: "#AFE1AF", fill: "white", minWidth: "20px", - minHeight: "20px" - } + minHeight: "20px", + }, }, dot: { rect: { @@ -251,14 +251,13 @@ export const styles = { width: "3px", height: "3px", x: 10, - y: 2 - } - } - } -} + y: 2, + }, + }, + }, +}; ``` - ```javascript const primaryColor = "white"; @@ -457,10 +456,9 @@ export const styles = { [![Edit custom-styles-novu-nc](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/custom-styles-novu-nc-2jd46m?fontsize=14&module=%2Fsrc%2Fstyles.js&moduleview=1&theme=dark) - ## Customization Using Hooks If you don't want to use our pre built component then, you can build your own notification center using hooks. `@novu/notification-center` react package provides many hooks like `useNotifications`, `useFetchNotifications`, `useUpdateUserPreferences`. Checkout all available [hooks](/notification-center/client/react/api-reference#hooks). All the hooks should be defined inside the [NovuProvider](/notification-center/client/react/api-reference#novuprovider) component. Most of the hooks are based on the `@tanstack/react-query` library. Checkout below example on how to use `useNotifications` and `useFetchNotifications` hook -[![Edit useNotifications-useFetchNotifications-hook](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/usenotifications-usefetchnotifications-hook-2t6sgz?fontsize=14&hidenavigation=1&theme=dark) \ No newline at end of file +[![Edit useNotifications-useFetchNotifications-hook](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/usenotifications-usefetchnotifications-hook-2t6sgz?fontsize=14&hidenavigation=1&theme=dark) diff --git a/notification-center/client/react/get-started.mdx b/inbox/react/get-started.mdx similarity index 76% rename from notification-center/client/react/get-started.mdx rename to inbox/react/get-started.mdx index 1a7b1352..8715f30c 100644 --- a/notification-center/client/react/get-started.mdx +++ b/inbox/react/get-started.mdx @@ -40,7 +40,6 @@ function Novu() { Go to this react app browser tab, there will be a bell icon. On clicking that bell icon, you will see a notification component popover - @@ -77,19 +76,18 @@ function Novu() { ``` ## Notification center without bell + If you don't want to show the bell icon, you can directly render the notification center component without passing the bell, use `` component in place of `` + ```jsx -import { - NovuProvider, - NotificationCenter, -} from "@novu/notification-center"; +import { NovuProvider, NotificationCenter } from "@novu/notification-center"; - +; ``` ## HMAC Encryption @@ -130,7 +128,11 @@ const hmacHash = createHmac("sha256", process.env.NOVU_API_KEY) ``` -If HMAC encryption is active in In-App provider settings and `subscriberHash` along with `subscriberId` is not provided, then notification center will not load + + If HMAC encryption is active in In-App provider settings and `subscriberHash` + along with `subscriberId` is not provided, then notification center will not + load + ## Notification Actions @@ -186,7 +188,6 @@ import { } from "@novu/notification-center"; export default function Novu() { - const CustomNotificationCenter = () => { const { updateAction } = useUpdateAction(); @@ -232,43 +233,6 @@ export default function Novu() { CTA buttons text and redirect url field supports dynamic variables - -## Avatar Icons - -Avatar images can be shown with in-app notifications. To do so, enable `Add an Avatar` option in in-app step editor as shown below - - - - - -**There are 3 ways to show the avatar images in the notifications:** - - - - - -### User Avatar -This option will show the avatar of the actor user. actor field can be sent while triggering the worklfow. Each subscriber has `avatar` field. For this option to work actor should have `avatar` field set with valid publicly accessible image URL. If the actor doesn't have `avatar` field, then the default avatar will be shown. Worklfow trigger example with avatar - -```javascript -await novu.trigger('', { - to: { - subscriberId: 'subscriberId_1', - }, - payload: { - "key": "value" - }, - actor: "subscriberId_2" -}); -``` -Here subscriberId_2 is used as actor and workflow is triggered to subscriberId_1 - -### Static Avatar Url -This option can be used if you want to show same, static and custom avatar icon for each in-app notification of this workflow in-app step. Make sure to toggle off **User Avatar** option to enable this. This is a input field where only valid publicly accessible image URL can be entered. Currently variables are not supported in this field. - -### System Avatar Icons -Novu supports 6 system avatar icons. These are default icons that can be selected as avatar icon. Once selected, same icon will be used as avatar icons for all notifications of this in-app step. - ## Realtime sockets Novu provides a real-time socket API for you to consume and get updates about new notifications added to the user's feed. To use the socket connection you can use the `useSocket` hook provided by the `@novu/notification-center` library. Let's see an example of that: @@ -322,53 +286,51 @@ Novu does not have a native taost component. However, if your project is using a socket.on("notification_received", (data) => { console.log(data); // set received notification content as toast content - setToastContent(data.content) + setToastContent(data.content); // open the toast - openToast(true) + openToast(true); }); ``` - ## Example [![Edit kind-nova-xv6s9g](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/kind-nova-xv6s9g?fontsize=14&hidenavigation=1&theme=dark) - ## Frequently Asked Questions - Possible reasons for the notification center not loading properly: +Possible reasons for the notification center not loading properly: - Invalid subscriberId - Invalid applicationIdentifier - Invalid subscriberHash (in case of active HMAC encryption) - Invalid backendUrl (in case of self-hosted) - Invalid socketUrl (in case of self-hosted) - + - The notification center should be wrapped in `` - +The notification center should be wrapped in `` + - The redirect URL is for the entire notification item, When the user clicks the notification, the user will be routed to that url. CTA are two call-to-action buttons. `onNotificationClick` function prop is used for redirecting url and the `onActionClick` function prop is used for CTA. - +The redirect URL is for the entire notification item, When the user clicks the notification, the user will be routed to that url. CTA are two call-to-action buttons. `onNotificationClick` function prop is used for redirecting url and the `onActionClick` function prop is used for CTA. + - + - Bell icon and notification center in Novu dashboard is only for receiving system related notifications like user you invited has joined, password reset request sent. It can not to used to test notification center. Consider using our [codesandbox example](https://codesandbox.io/s/kind-nova-xv6s9g?fontsize=14&hidenavigation=1&theme=dark) instead. - +Bell icon and notification center in Novu dashboard is only for receiving system related notifications like user you invited has joined, password reset request sent. It can not to used to test notification center. Consider using our [codesandbox example](https://codesandbox.io/s/kind-nova-xv6s9g?fontsize=14&hidenavigation=1&theme=dark) instead. + Our notification center is configures for all hosts. Please disable root level `withCredentials` config in axios or fetch http requests. - + diff --git a/notification-center/client/react/multiple-tabs.mdx b/inbox/react/multiple-tabs.mdx similarity index 92% rename from notification-center/client/react/multiple-tabs.mdx rename to inbox/react/multiple-tabs.mdx index c10acf1f..e063c580 100644 --- a/notification-center/client/react/multiple-tabs.mdx +++ b/inbox/react/multiple-tabs.mdx @@ -30,16 +30,16 @@ To create multiple stores you can use the prop `stores` on the `NovuProvider` Using the `query` object multiple fields can be passed for feed API: -- `feedIdentifier` - Can be configured and created on the WEB UI +- `feedIdentifier` - Can be configured and created on the Dashboard UI - `seen` - Identifies if the notification has been seen or not After specifying the `stores` prop, you can use the `storeId` property to interact with the store. -### Managing feeds on the web UI +### Managing feeds on the Dashboard UI 1. How to add a new feed or select existing feed for In-App step? -To add a new feed, enable *Use Feeds* checkbox in In-App editor. Select from existing feeds or create a new feed by writing in the input field and then clicking on plus icon in the right side. +To add a new feed, enable _Use Feeds_ checkbox in In-App editor. Select from existing feeds or create a new feed by writing in the input field and then clicking on plus icon in the right side. @@ -54,7 +54,8 @@ To copy `feedIdentifier` (`storeId`) click on the feed name of existing feeds. c -By specifying only a storeId, without a query, you could get all notifications. + By specifying only a storeId, without a query, you could get all + notifications. ### Using `tabs` prop on the notification center diff --git a/notification-center/client/react/preference.mdx b/inbox/react/preference.mdx similarity index 100% rename from notification-center/client/react/preference.mdx rename to inbox/react/preference.mdx diff --git a/framework/integrations/react-email.mdx b/integrations/content/react-email.mdx similarity index 84% rename from framework/integrations/react-email.mdx rename to integrations/content/react-email.mdx index b47fed7d..840aefbd 100644 --- a/framework/integrations/react-email.mdx +++ b/integrations/content/react-email.mdx @@ -1,8 +1,16 @@ --- -title: "React E-mail" +title: "React Email" --- -Integrating Novu Framework with [React.Email](https://react.email/) for your Next.js application can be done in three steps. If you don't have an app, you can create one with the `create-novu-app` command. +Integrating Novu Framework with [React.Email](https://react.email/) for your Next.js application can be done in three steps. + + If you don't have an app, you can create one with the `create-novu-app` command. + +```bash +npx create-novu-app@latest --api-key= +``` + + @@ -12,6 +20,7 @@ Integrating Novu Framework with [React.Email](https://react.email/) for your Nex ```bash npm i @react-email/components react-email ``` + Create a new `sample-email.tsx` file for your email template. @@ -37,7 +46,7 @@ Integrating Novu Framework with [React.Email](https://react.email/) for your Nex return render(); } ``` - + Define your Workflow using the above template @@ -55,5 +64,6 @@ Integrating Novu Framework with [React.Email](https://react.email/) for your Nex }); }); ``` + diff --git a/framework/integrations/remix-react-email.mdx b/integrations/content/remix-react-email.mdx similarity index 98% rename from framework/integrations/remix-react-email.mdx rename to integrations/content/remix-react-email.mdx index 3e1c552c..b68f68b1 100644 --- a/framework/integrations/remix-react-email.mdx +++ b/integrations/content/remix-react-email.mdx @@ -1,5 +1,5 @@ --- -title: "Remix w/ React E-mail" +title: "Remix & React Email" --- Integrating Novu Framework with [React email](https://react.email/) for your Remix application can be done in three steps. If you don't have an app, you can [clone our Remix example](https://github.com/novuhq/novu-framework-remix-example). @@ -11,6 +11,7 @@ Integrating Novu Framework with [React email](https://react.email/) for your Rem ```bash npm i @react-email/components react-email ``` + Create an `emails` folder in the `app` directory of your Remix app. @@ -38,6 +39,7 @@ Integrating Novu Framework with [React email](https://react.email/) for your Rem return render(); } ``` + Define your Workflow using the above template @@ -55,5 +57,6 @@ Integrating Novu Framework with [React email](https://react.email/) for your Rem }); }); ``` + diff --git a/framework/integrations/svelte-email.mdx b/integrations/content/svelte-email.mdx similarity index 99% rename from framework/integrations/svelte-email.mdx rename to integrations/content/svelte-email.mdx index f9cfdbfd..4ddf505c 100644 --- a/framework/integrations/svelte-email.mdx +++ b/integrations/content/svelte-email.mdx @@ -1,5 +1,5 @@ --- -title: "Svelte E-mail" +title: "Svelte Email" --- Integrating Novu Framework with [Svelte email](https://react.email/) for your Svelte application can be done in three steps. If you don't have an app, you can [clone our Svelte example](https://github.com/novuhq/novu-svelte-email). @@ -11,6 +11,7 @@ Integrating Novu Framework with [Svelte email](https://react.email/) for your Sv ```bash npm i svelte-email ``` + Create an `emails` folder in the `src/lib` directory of your Svelte app. @@ -99,6 +100,7 @@ Integrating Novu Framework with [Svelte email](https://react.email/) for your Sv ``` + Define your Workflow using the above template @@ -126,5 +128,6 @@ Integrating Novu Framework with [Svelte email](https://react.email/) for your Sv } ); ``` + diff --git a/framework/integrations/vue-email.mdx b/integrations/content/vue-email.mdx similarity index 99% rename from framework/integrations/vue-email.mdx rename to integrations/content/vue-email.mdx index 3eeb4c5d..6dc853c9 100644 --- a/framework/integrations/vue-email.mdx +++ b/integrations/content/vue-email.mdx @@ -1,5 +1,5 @@ --- -title: "Vue E-mail" +title: "Vue Email" --- You can integrate Novu Framework with [Vue Email](https://vuemail.net/) in a few simple steps. This guide will walk you through the process of creating a new email template using Vue Email and Nuxt. @@ -93,6 +93,7 @@ For a Quickstart Boilerplate project using Nuxt.js, and Vue Email, check out the }); ``` + ## Learn More diff --git a/channels-and-providers/chat/discord.mdx b/integrations/providers/chat/discord.mdx similarity index 92% rename from channels-and-providers/chat/discord.mdx rename to integrations/providers/chat/discord.mdx index 9443fedb..12d76b20 100644 --- a/channels-and-providers/chat/discord.mdx +++ b/integrations/providers/chat/discord.mdx @@ -1,6 +1,6 @@ --- -title: 'Discord' -description: 'Learn about how to use Discord provider for chat notifications' +title: "Discord" +description: "Learn about how to use Discord provider for chat notifications" --- When using Discord, you will have to store the integration credentials within the subscriber entity. Discord supports two ways to do this: @@ -19,7 +19,7 @@ To get started with using Novu’s Discord Webhook integration, you need a ‘we 3. Integrations -> Webhooks -> New Webhook 4. Copy the webhook URL. 5. After obtaining the webhook URL in the previous step, you need to store it within the subscriber entity. Doing this ensures that Novu knows where (in which discord channel) to send the notification to. Here’s how to do it: - + ```javaScript @@ -31,12 +31,13 @@ import { const novu = new Novu(""); await novu.subscribers.setCredentials('subscriberId', ChatProviderIdEnum.Discord, { - webhookUrl: "", +webhookUrl: "", }); -``` + +```` -```bash +```bash curl -L -X PUT 'https://api.novu.co/v1/subscribers//credentials' \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ @@ -48,13 +49,15 @@ curl -L -X PUT 'https://api.novu.co/v1/subscribers//credentials' }, "integrationIdentifier": "discord-MnGLxp8uy" }' -``` +```` + Checkout the [API reference](/api-reference/subscribers/update-subscriber-credentials) for more details. - + - `subscriberId` is a custom identifier used when identifying your users within the Novu platform. - `providerId` is a unique provider identifier. We recommend using our ChatProviderIdEnum to specify the provider. - The third parameter is the credentials object. In this case, we use the `webhookUrl` property to specify the webhook URL generated in the previous step. -6. You are all set up and ready to send your first chat message via our `@novu/node` package or directly using the REST API in the channel you chose on your discord server. \ No newline at end of file + +6. You are all set up and ready to send your first chat message via our `@novu/node` package or directly using the REST API in the channel you chose on your discord server. diff --git a/channels-and-providers/chat/mattermost.mdx b/integrations/providers/chat/mattermost.mdx similarity index 66% rename from channels-and-providers/chat/mattermost.mdx rename to integrations/providers/chat/mattermost.mdx index fb5baf66..41339639 100644 --- a/channels-and-providers/chat/mattermost.mdx +++ b/integrations/providers/chat/mattermost.mdx @@ -1,6 +1,6 @@ --- -title: 'Mattermost' -description: 'Learn about how to use Mattermost provider for chat notifications' +title: "Mattermost" +description: "Learn about how to use Mattermost provider for chat notifications" --- When using Mattermost, you will have to store the integration credentials within the subscriber entity. Mattermost supports two ways to do this: @@ -8,7 +8,6 @@ When using Mattermost, you will have to store the integration credentials within 1. Using the **Mattermost Webhook** integration. 2. Using the **Mattermost Bot** integration. - ### Mattermost Webhook Integration To integrate Mattermost with your application using the Mattermost Webhook integration, follow these steps: @@ -20,26 +19,27 @@ To integrate Mattermost with your application using the Mattermost Webhook integ Once you have the webhook URL, you can store it in the subscriber entity in your application. This will allow you to send notifications to Mattermost using the following code: ```jsx -import { Novu, ChatProviderIdEnum } from '@novu/node'; +import { Novu, ChatProviderIdEnum } from "@novu/node"; const novu = new Novu(""); -await novu.subscribers.setCredentials('subscriberId', ChatProviderIdEnum.Mattermost, { webhookUrl: '', }); +await novu.subscribers.setCredentials( + "subscriberId", + ChatProviderIdEnum.Mattermost, + { webhookUrl: "" } +); // Send a notification to Mattermost using the subscriber ID and payload. -await novu.trigger('', - { - to: { - subscriberId: '', - }, - payload: { - message: 'This is a notification from my application!' - }, - } -); +await novu.trigger("", { + to: { + subscriberId: "", + }, + payload: { + message: "This is a notification from my application!", + }, +}); ``` - ### Mattermost Bot Integration To integrate Mattermost with your application using the Mattermost Bot integration, you will need to create a Mattermost bot account and generate an API token for the bot. Once you have the API token, you can store it in the subscriber entity in your application. @@ -47,20 +47,24 @@ To integrate Mattermost with your application using the Mattermost Bot integrati Once you have stored the API token in the subscriber entity, you can send notifications to Mattermost using the following code: ```jsx -import { Novu, ChatProviderIdEnum } from '@novu/node'; +import { Novu, ChatProviderIdEnum } from "@novu/node"; const novu = new Novu(""); // Get the Mattermost bot API token for the subscriber. -await novu.subscribers.setCredentials('subscriberId', ChatProviderIdEnum.Mattermost, { botApiToken: '' }); +await novu.subscribers.setCredentials( + "subscriberId", + ChatProviderIdEnum.Mattermost, + { botApiToken: "" } +); // Send a notification to Mattermost using the bot API token. -await novu.trigger('', { +await novu.trigger("", { to: { - subscriberId: '', + subscriberId: "", }, payload: { - message: 'This is a notification from my application!', + message: "This is a notification from my application!", }, }); ``` diff --git a/channels-and-providers/chat/ms-teams.mdx b/integrations/providers/chat/ms-teams.mdx similarity index 82% rename from channels-and-providers/chat/ms-teams.mdx rename to integrations/providers/chat/ms-teams.mdx index 73defcf0..35980b47 100644 --- a/channels-and-providers/chat/ms-teams.mdx +++ b/integrations/providers/chat/ms-teams.mdx @@ -1,9 +1,9 @@ --- -title: 'MS Teams' -description: 'Learn about how to use MS Teams provider for chat notifications' +title: "MS Teams" +description: "Learn about how to use MS Teams provider for chat notifications" --- -import ProviderImplementation from '/snippets/provider-implementation.mdx'; +import ProviderImplementation from "/snippets/provider-implementation.mdx"; MS Teams does not need any `API Key` or `Client ID` to send notifications. Our current implementation is based on the [Incoming Webhook URL](https://learn.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/add-incoming-webhook?tabs=newteams%2Cjavascript). It is a URL that you can use to send messages to a specific channel. This URL needs to be stored on the subscriber entity @@ -13,7 +13,9 @@ Similar to [Discord](https://www.notion.so/Additional-Resources-65ef4aca5d0f4115 Checkout step by step instructions on microsoft team's documentation on how to [create an incoming webhook url](https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/add-incoming-webhook?tabs=newteams%2Cjavascript). - + + {" "} + ## Storing webhook url on subscriber entity @@ -33,15 +35,16 @@ import { const novu = new Novu(""); await novu.subscribers.setCredentials( - 'subscriberId', - ChatProviderIdEnum.MsTeams, // providerId - { webhookUrl: "" }, - 'msteams-MnGLxp8uy' // integration identifier +'subscriberId', +ChatProviderIdEnum.MsTeams, // providerId +{ webhookUrl: "" }, +'msteams-MnGLxp8uy' // integration identifier ); -``` + +```` -```bash +```bash curl -L -X PUT 'https://api.novu.co/v1/subscribers//credentials' \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ @@ -53,7 +56,8 @@ curl -L -X PUT 'https://api.novu.co/v1/subscribers//credentials' }, "integrationIdentifier": "msteams-MnGLxp8uy" }' -``` +```` + @@ -63,5 +67,4 @@ Checkout the [API reference](/api-reference/subscribers/update-subscriber-creden - `providerId` is a unique provider identifier. We recommend using our ChatProviderIdEnum to specify the provider. - The third parameter is the credentials object, in this case, we use the `webhookUrl` property to specify the MS Teams channel webhook URL or by calling the `Update Subscriber Credentials` endpoint on Novu API. Check endpoint details [here](https://docs.novu.co/api/update-subscriber-credentials/). - - \ No newline at end of file + diff --git a/channels-and-providers/chat/overview.mdx b/integrations/providers/chat/overview.mdx similarity index 70% rename from channels-and-providers/chat/overview.mdx rename to integrations/providers/chat/overview.mdx index 8fe9daea..f8f30c00 100644 --- a/channels-and-providers/chat/overview.mdx +++ b/integrations/providers/chat/overview.mdx @@ -1,22 +1,27 @@ --- -title: 'Chat Channel Overview' -sidebarTitle: 'Overview' -description: 'Learn the process of configuring and using chat providers with Novu' +title: "Chat Channel Overview" +sidebarTitle: "Overview" +description: "Learn the process of configuring and using chat providers with Novu" --- import { MissingProvider } from "/snippets/missing-provider.mdx"; Novu can be used to deliver chat messages to your customers using a unified delivery API. You can easily integrate your favorite chat provider using the built-in integration store. - + ## Configuring chat providers -To configure a chat provider, you need to add a new integration to your Novu account. You can do this by navigating to the integration store and clicking on the **Add a provider** button and then select the chat provider you want to configure. You can find the [integration store](https://web.novu.co/integrations?utm_campaign=docs-chat-overview) in the left sidebar of the Novu dashboard. +To configure a chat provider, you need to add a new integration to your Novu account. You can do this by navigating to the integration store and clicking on the **Add a provider** button and then select the chat provider you want to configure. You can find the [integration store](https://dashboard.novu.co/integrations?utm_campaign=docs-chat-overview) in the left sidebar of the Novu dashboard. Each chat provider requires defferent type of credentials. There are few providers which does not require any credentials example: `Discord` - ## Sending chat message Steps to send a chat message using Novu:- @@ -26,7 +31,7 @@ Steps to send a chat message using Novu:- > Step 2 can be skipped if you already have a subscriber. You can use the `subscriberId` of an existing subscriber. -3. [Update](/channels-and-providers/chat/overview#update-credential-webhookurl) the subscriber credential `webhookUrl` for this provider and integration. +3. [Update](/channels-and-providers/chat/overview#update-credential-webhookurl) the subscriber credential `webhookUrl` for this provider and integration. 4. Create a new [worklow](/workflows/notification-workflows) or use an existing workflow. Add chat channel and [write content](/content-creation-design/notification-content-creation#chat) for the message. @@ -47,12 +52,13 @@ import { const novu = new Novu(""); await novu.subscribers.setCredentials('subscriberId', ChatProviderIdEnum.Slack, { - webhookUrl: "", +webhookUrl: "", }); -``` + +```` -```bash +```bash curl -L -X PUT 'https://api.novu.co/v1/subscribers//credentials' \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ @@ -64,26 +70,31 @@ curl -L -X PUT 'https://api.novu.co/v1/subscribers//credentials' }, "integrationIdentifier": "slack-MnGLxp8uy" }' -``` +```` + Checkout the [API reference](/api-reference/subscribers/update-subscriber-credentials) for more details. - Integration identifier is similar to Provider identifier but it is different than Provider Id. It is unique for each integration. You can find the `integrationIdentifier` in the integration store page. + + {" "} + Integration identifier is similar to Provider identifier but it is different than + Provider Id. It is unique for each integration. You can find the `integrationIdentifier` + in the integration store page.{" "} + ## Common errors Common erros and reason of those errors while sending chat messages using Novu. 1. Subscriber does not have a configured channel. - - if the subscriber does not have credentials set up for any of the chat provider. + - if the subscriber does not have credentials set up for any of the chat provider. 2. Webhook URL for the chat channel is missing. - - `webhookUrl` field is null, undefined or not set. Check using [get subscriber api](/api-reference/subscribers/get-subscriber). + - `webhookUrl` field is null, undefined or not set. Check using [get subscriber api](/api-reference/subscribers/get-subscriber). 3. Subscriber does not have an active integration. - - if subscriber have credentials set but integration is either not active or deleted for this credential. + - if subscriber have credentials set but integration is either not active or deleted for this credential. - + - diff --git a/channels-and-providers/chat/slack.mdx b/integrations/providers/chat/slack.mdx similarity index 63% rename from channels-and-providers/chat/slack.mdx rename to integrations/providers/chat/slack.mdx index c815a4eb..cf284f43 100644 --- a/channels-and-providers/chat/slack.mdx +++ b/integrations/providers/chat/slack.mdx @@ -1,6 +1,6 @@ --- -title: 'Slack' -description: 'Learn about how to use Slack provider for chat notifications' +title: "Slack" +description: "Learn about how to use Slack provider for chat notifications" --- When using Slack you will have to save the integration credentials in the subscriber entity. @@ -9,7 +9,10 @@ This guide will walk you through the steps needed to obtain the `webhookUrl` t We will provide the basic flow that the user needs to perform, to successfully send notifications via the Slack integration. -We've a [dedicated guide](/guides/slack-guide) on integrating Novu in a Slack app. + + We've a [dedicated guide](/guides/slack-guide) on integrating Novu in a Slack + app. + ### Creating application @@ -28,21 +31,27 @@ If you use this approach, then Novu will manage the OAuth flow and store the cre 1. Configure your Slack application as mentioned [below](#configuring-slack-application). 2. You need to make a network request to the `Shareable URL` to perform authentication. -3. You can find the `Shareable URL` in the [Integration Store](https://web.novu.co/integrations?utm_campaign=docs-integrations-slack) - -4. You can either add the `Add to Slack` button or use the `Shareable URL` directly, in the application you'll be using Novu in. -The network request to the 'Shareable URL' will happen when the user clicks the 'Add to Slack' button. +3. You can find the `Shareable URL` in the [Integration Store](https://dashboard.novu.co/integrations?utm_campaign=docs-integrations-slack) + + {" "} + +4. You can either add the `Add to Slack` button or use the `Shareable URL` directly, in the application you'll be using Novu in. + + The network request to the 'Shareable URL' will happen when the user clicks + the 'Add to Slack' button. + 5. This is to request permission for access (scope: incoming-webhook). -6. You can use the generated `Shareable URL` that is found under the Slack integration in the [Integration Store](https://web.novu.co/integrations?utm_campaign=docs-integrations-slack). The `Shareable URL` should have the following format: +6. You can use the generated `Shareable URL` that is found under the Slack integration in the [Integration Store](https://dashboard.novu.co/integrations?utm_campaign=docs-integrations-slack). The `Shareable URL` should have the following format: + ```bash https://api.novu.co/v1/subscribers//credentials/slack/oauth?environmentId=&integrationIdentifier=. ``` - `SUBSCRIBER_ID` is a custom identifier used when identifying your users within the Novu platform. -- `ENVIRONMENT_ID` is a context of an environment you can locate your environment id in the setting in the [dashboard settings](https://web.novu.co/settings?utm_campaign=docs-integrations-slack). -- `PROVIDER_ID` (optional) is a unique identifier of the integration and is required when you have multiple slack integrations in Novu. You can locate your integration identifier in the [Integration Store](https://web.novu.co/integrations?utm_campaign=docs-integratons-slack). When not provided, the last created integration will be used. -If you are using the `Add to Slack` button you have to provide the `Shareable URL` as the `redirect_uri` parameter like in this example. Make sure that the `Shareable URL` is URL encoded: +- `ENVIRONMENT_ID` is a context of an environment you can locate your environment id in the setting in the [dashboard settings](https://dashboard.novu.co/settings?utm_campaign=docs-integrations-slack). +- `PROVIDER_ID` (optional) is a unique identifier of the integration and is required when you have multiple slack integrations in Novu. You can locate your integration identifier in the [Integration Store](https://dashboard.novu.co/integrations?utm_campaign=docs-integratons-slack). When not provided, the last created integration will be used. + If you are using the `Add to Slack` button you have to provide the `Shareable URL` as the `redirect_uri` parameter like in this example. Make sure that the `Shareable URL` is URL encoded: ```html ``` + 7. Either you use the 'add to Slack' button or use the `Shareable URL`, you should land here: - -8. Then you'll be redirected to the address you chose in the [Integration Store](https://web.novu.co/integrations?utm_campaign=docs-integrations-slack): - -You can use the URL of whichever page you want your users to land on, after successful execution + + {" "} + +8. Then you'll be redirected to the address you chose in the [Integration Store](https://dashboard.novu.co/integrations?utm_campaign=docs-integrations-slack): + + {" "} + + + You can use the URL of whichever page you want your users to land on, after + successful execution + 9. That's it! Now you can trigger your workflow and send notifications: - -Don't forget to check out our [dedicated guide](/guides/slack-guide) on integrating Novu in a Slack app for more. + + {" "} + + + Don't forget to check out our [dedicated guide](/guides/slack-guide) on + integrating Novu in a Slack app for more. + + ### 2. Manually managed -To use the manually managed option, you need to generate a `webhookUrl` and plug it into your backend. + +To use the manually managed option, you need to generate a `webhookUrl` and plug it into your backend. + 1. Goto 'Incoming Webhooks' in your Slack app settings and turn it on. - + + {" "} + 2. Click on the 'Add New Webhook to Workspace': - + + {" "} + 3. Now, go ahead and select the channel in which you want to send notifications and click 'allow'. - + + {" "} + 4. Then, copy the 'webhookUrl' from Slack. - + + {" "} + 5. Now, you need to save the `webhookUrl` on the relevant subscriber entity in Novu. Here's an example to do the same using our Node SDK: ## Update credential webhookUrl @@ -91,12 +124,13 @@ import { const novu = new Novu(""); await novu.subscribers.setCredentials('subscriberId', ChatProviderIdEnum.Slack, { - webhookUrl: "", +webhookUrl: "", }); -``` + +```` -```bash +```bash curl -L -X PUT 'https://api.novu.co/v1/subscribers//credentials' \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ @@ -108,7 +142,8 @@ curl -L -X PUT 'https://api.novu.co/v1/subscribers//credentials' }, "integrationIdentifier": "slack-MnGLxp8uy" }' -``` +```` + @@ -123,8 +158,8 @@ Checkout the [API reference](/api-reference/subscribers/update-subscriber-creden ## Configuring Slack application 1. Go to OAuth & Permissions on Slack's Developer Dashboard and add your REDIRECT_URL in Redirect URLs. - - If you use a manual Management solution, add the API endpoint you created in Step 1. - - If you use the Novu Managed solution add `https://api.novu.co/v1/subscribers`. + - If you use a manual Management solution, add the API endpoint you created in Step 1. + - If you use the Novu Managed solution add `https://api.novu.co/v1/subscribers`. 2. Go to Incoming Webhooks from the left menu and Activate Incoming Webhooks. 3. Go to Manage Distribution and at the bottom of the page, tick Remove Hard Coded Information and Activate Public Distribution. @@ -134,16 +169,15 @@ To enable Hash-Based Message Authentication Codes, you need to do the following 1. Visit the integration store page and enable HMAC encryption under your chat provider. 2. The next step would be to generate an HMAC encrypted subscriberId on your backend: - - ```jsx - import { createHmac } from 'crypto'; - - const hmacHash = createHmac('sha256', process.env.NOVU_API_KEY) - .update(subscriberId) - .digest('hex'); - - ``` - + + ```jsx + import { createHmac } from "crypto"; + + const hmacHash = createHmac("sha256", process.env.NOVU_API_KEY) + .update(subscriberId) + .digest("hex"); + ``` + 3. Add the newly created hash HMAC to the Sharable URL as a query. This concludes the Slack provider guide. diff --git a/channels-and-providers/chat/whats-app.mdx b/integrations/providers/chat/whats-app.mdx similarity index 77% rename from channels-and-providers/chat/whats-app.mdx rename to integrations/providers/chat/whats-app.mdx index 99322061..3c24681a 100644 --- a/channels-and-providers/chat/whats-app.mdx +++ b/integrations/providers/chat/whats-app.mdx @@ -1,7 +1,7 @@ --- -title: 'How to send WhatsApp Business notifications with Novu' -sidebarTitle: 'WhatsApp Business' -description: 'Learn about how to use WhatsApp for chat notifications' +title: "How to send WhatsApp Business notifications with Novu" +sidebarTitle: "WhatsApp Business" +description: "Learn about how to use WhatsApp for chat notifications" --- ## Getting Started @@ -21,12 +21,14 @@ On the App Setup page, click on "Set Up" under the "WhatsApp" product. You will ### Step 3: Send a Sandbox Message Copy the following pieces and paste them in the Novu WhatsApp Business integration settings: + - Temporary Access Token - Access API token Field - Phone Number ID - Phone Number Identification Field -It's important to note that the test credentials for whatsapp cannot be used in production and will expire in 24 hours. - You will need to submit your app for review to obtain production credentials. + It's important to note that the test credentials for whatsapp cannot be used + in production and will expire in 24 hours. You will need to submit your app + for review to obtain production credentials. ### Step 4: Add a Test Phone Number @@ -63,12 +65,13 @@ and in the `overrides` field, add the following: ``` - For test credentials you can only used the built in Whats App Template. + For test credentials you can only used the built in Whats App Template. ## Going to Production ### Register a Business Phone Number + To go live you will need to add a real business phone number and submit your app for review. Follow the [Facebook Instructions](https://developers.facebook.com/docs/whatsapp/cloud-api/get-started/add-a-phone-number) on how to proceed. @@ -77,7 +80,6 @@ Follow the [Facebook Instructions](https://developers.facebook.com/docs/whatsapp Follow the [Facebook Instructions](https://developers.facebook.com/docs/whatsapp/business-management-api/get-started/) on how to generate a permanent access token. Depending on your use case. - ### Create a WhatsApp Template You will need to create a WhatsApp Template to send notifications to your customers. Create a template in the [Business Manager](https://business.facebook.com/wa/manage/message-templates) and submit it for review. @@ -86,21 +88,21 @@ After your template is approved, you can use the `template_name` to send notific To send a notification with a template, you will need to add the following to the `overrides` field: ```typescript -novu.trigger('workflow-id', { - to: { - subscriberId: "SUBSCRIBER_ID", - phone: "+11111111111" +novu.trigger("workflow-id", { + to: { + subscriberId: "SUBSCRIBER_ID", + phone: "+11111111111", + }, + payload: {}, + overrides: { + chat: { + template: { + name: "template_name", + language: { + code: "en_US", + }, + }, }, - payload: {}, - overrides: { - chat: { - template: { - name: "template_name", - language: { - code: "en_US" - } - } - } - } -}) + }, +}); ``` diff --git a/channels-and-providers/chat/zulip.mdx b/integrations/providers/chat/zulip.mdx similarity index 82% rename from channels-and-providers/chat/zulip.mdx rename to integrations/providers/chat/zulip.mdx index 228c5281..533a9bb2 100644 --- a/channels-and-providers/chat/zulip.mdx +++ b/integrations/providers/chat/zulip.mdx @@ -1,6 +1,6 @@ --- -title: 'Zulip' -description: 'Learn about how to use Zulip provider for chat notifications' +title: "Zulip" +description: "Learn about how to use Zulip provider for chat notifications" --- Zulip does not need any API Key or client ID to push messages in it. All it needs is the webhook URL of the channel you want to send messages to. That itself acts as the credential. @@ -13,19 +13,27 @@ Similar to Discord, the credential for this provider needs to be stored in the s - Click on the Settings icon in the top right corner of the screen, and then click "Personal settings" from the drop-down menu. - + + + - Click "Add a new bot" button in "Bots" tab. - + + {" "} + - Set bot type as "Incoming webhook". - + + {" "} + - Click the small link icon to generate webhook URL for provider. Set Integration as `Slack compatible webhook`, choose your channel and copy webhook URL. - + + {" "} + ## Connecting our subscribers to Zulip @@ -48,12 +56,13 @@ import { const novu = new Novu(""); await novu.subscribers.setCredentials('subscriberId', ChatProviderIdEnum.Zulip, { - webhookUrl: "", +webhookUrl: "", }); -``` + +```` -```bash +```bash curl -L -X PUT 'https://api.novu.co/v1/subscribers//credentials' \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ @@ -65,7 +74,8 @@ curl -L -X PUT 'https://api.novu.co/v1/subscribers//credentials' }, "integrationIdentifier": "zulip-MnGLxp8uy" }' -``` +```` + diff --git a/integrations/providers/default-providers.mdx b/integrations/providers/default-providers.mdx new file mode 100644 index 00000000..0c312651 --- /dev/null +++ b/integrations/providers/default-providers.mdx @@ -0,0 +1,55 @@ +--- +title: "Novu Providers" +description: "Learn about the default Novu email and sms providers" +--- + +import CloudOnlyFeature from "/snippets/cloud-only-feature.mdx"; + +## Novu Providers + +To help you evaluate our services better, Novu provides an email and sms provider by default for every account. After signing up, you can go to the [Integrations store](https://dashboard.novu.co/integrations?utm_campaign=docs-default-providers) on the Novu web dashboard to see this. + + + +## Novu Email Provider + +If you've a newly signed-up account on Novu, it will look like this: + + + {" "} + + +## Novu SMS Provider + +And this is what the default Novu SMS provider looks like: + + + {" "} + + + + The default email and sms providers are for evaluation purposes only and their + use in production-grade apps is not recommended. You should switch to any of + our [numerous other providers](/additional-resources/glossary#3-providers) for + production apps. Also, there are two modes for default Novu providers - + Development and Production. + + +## Novu In-app Provider + +In addition to email and sms, we also have in-app provider: + + + {" "} + + +It is the only provider for the in-app channel and it _can be used in production apps_. Unlike sms and email, it is not active by default and needs to be turned on separately. It can be scaled as per your need and it is a 'pay as you go' offering. You can check [this](https://novu.co/pricing/?utm_campaign=docs-default-providers) for more details. + +## Limits of the Novu Providers + +Following are the limits for our email and sms providers: + +1. Email: 300 emails per organization per month +2. SMS: 20 messages per organization per month + +To send more than these limits, you can configure a different provider for a specific channel. diff --git a/channels-and-providers/email/amazon-ses.mdx b/integrations/providers/email/amazon-ses.mdx similarity index 74% rename from channels-and-providers/email/amazon-ses.mdx rename to integrations/providers/email/amazon-ses.mdx index dc8980ad..dcf31ecf 100644 --- a/channels-and-providers/email/amazon-ses.mdx +++ b/integrations/providers/email/amazon-ses.mdx @@ -1,6 +1,6 @@ --- -title: 'Amazon SES' -description: 'Learn how to use the Amazon SES provider to send email notifications using Novu' +title: "Amazon SES" +description: "Learn how to use the Amazon SES provider to send email notifications using Novu" --- You can use the [Amazon SES](https://aws.amazon.com/ses/) provider to send transactional emails to your customers using the Novu Platform with a single API to create multi-channel experiences. @@ -23,14 +23,16 @@ To use the Amazon SES provider in the email channel, you will need to create an ## Creating an SES integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-integrations-ses) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-integrations-ses) page on Novu. - Click on Add a Provider. - Select Amazon SES service. - Enter previously saved `ACCESS_KEY_ID` and `ACCESS_SECRET_KEY`. - Fill in the `From email address` field using the authenticated sender email id in the previous step. - Enter `region` and `Sender name` also. -Example region format:- `us-east-1`. By default Novu uses `us-east-1` region. + + Example region format:- `us-east-1`. By default Novu uses `us-east-1` region. + - Click on the `Disabled` button and mark it as `Active`. - Click on the **Update** button. @@ -40,11 +42,9 @@ To use the Amazon SES provider in the email channel, you will need to create an - Possible reasons: - 1. You have not verified the subscriber's email address in SES (if you are in a sandbox environment). - 2. Your daily sending limit has been reached (if you are in a sandbox environment). - 3. You have entered the wrong aws region in the integration form. + Possible reasons: 1. You have not verified the subscriber's email address in + SES (if you are in a sandbox environment). 2. Your daily sending limit has + been reached (if you are in a sandbox environment). 3. You have entered the + wrong aws region in the integration form. - - diff --git a/channels-and-providers/email/braze.mdx b/integrations/providers/email/braze.mdx similarity index 82% rename from channels-and-providers/email/braze.mdx rename to integrations/providers/email/braze.mdx index f7e89ffc..9a9d75c3 100644 --- a/channels-and-providers/email/braze.mdx +++ b/integrations/providers/email/braze.mdx @@ -1,6 +1,6 @@ --- -title: 'Braze' -description: 'Learn how to use the Braze provider to send email notifications using Novu' +title: "Braze" +description: "Learn how to use the Braze provider to send email notifications using Novu" --- You can use the [Braze](https://braze.com/) provider to send transactional emails to your customers using the Novu Platform with a single API to create multi-channel experiences. @@ -19,10 +19,9 @@ To generate a new API key in Braze, you can follow these steps: - Give your new key a name for identification at a glance. - Select the right permission you want to be associated with the new API key. - # Creating a Braze integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-braze) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-braze) page on Novu. - Click on `Add a Provider`. - Locate **Braze** under the Email section and click on the **Next** button. - Select Your Environment add condition (Optional). @@ -32,4 +31,4 @@ To generate a new API key in Braze, you can follow these steps: - Enter the `Base URL`. - Enter the `From email address`. - Enter the `Sender name`. -- Click on the **Update** button. \ No newline at end of file +- Click on the **Update** button. diff --git a/channels-and-providers/email/custom-smtp.mdx b/integrations/providers/email/custom-smtp.mdx similarity index 75% rename from channels-and-providers/email/custom-smtp.mdx rename to integrations/providers/email/custom-smtp.mdx index 1adf0bfe..bad0435a 100644 --- a/channels-and-providers/email/custom-smtp.mdx +++ b/integrations/providers/email/custom-smtp.mdx @@ -1,6 +1,6 @@ --- -title: 'Custom SMTP' -description: 'Learn how to use the Custom SMTP provider to send email notifications using Novu' +title: "Custom SMTP" +description: "Learn how to use the Custom SMTP provider to send email notifications using Novu" --- You can use a Custom SMTP provider like [Nodemailer](https://nodemailer.com/about/) to send transactional emails through your custom SMTP server to your customers using the Novu Platform with a single API. @@ -23,17 +23,17 @@ Those options are: # Creating a Custom SMTP integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-customsmtp) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-customsmtp) page on Novu. - Click on Add a Provider. - Select Custom SMTP service. - Enter your SMTP credentials - - `host` - - `port` - - `username` - - `password` - - `secure` (on demand) - - And `DKIM` options if you want to sign messages with *DKIM* + - `host` + - `port` + - `username` + - `password` + - `secure` (on demand) + - And `DKIM` options if you want to sign messages with *DKIM* - Fill in the `From email address` field using the authenticated email from the previous step. - Click on the `Disabled` button and mark it as `Active`. - Click on the **Update** button. -- You should now be able to send notifications using Custom SMTP in Novu. \ No newline at end of file +- You should now be able to send notifications using Custom SMTP in Novu. diff --git a/channels-and-providers/email/infobip.mdx b/integrations/providers/email/infobip.mdx similarity index 76% rename from channels-and-providers/email/infobip.mdx rename to integrations/providers/email/infobip.mdx index 82cf7a2b..8df2304d 100644 --- a/channels-and-providers/email/infobip.mdx +++ b/integrations/providers/email/infobip.mdx @@ -1,6 +1,6 @@ --- -title: 'Infobip - email' -description: 'Learn how to use the Infobip provider to send email notifications using Novu' +title: "Infobip - email" +description: "Learn how to use the Infobip provider to send email notifications using Novu" --- You can use the [Infobip](https://www.infobip.com/developers/) provider to send transactional emails to your customers using the Novu Platform with a single API to create multi-channel experiences. @@ -18,14 +18,14 @@ To generate a new API key in Infobip, you can follow these steps: # Creating an Infobip integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-infobip) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-infobip) page on Novu. - Click on Add a Provider. - Select Infobip service. - Enter your Infobip API Key. - Enter your Base URL. - - To see your base URL, log in to the account. Once logged in, on all pages, you should see your base URL in this format: `xxxxx.api.infobip.com`. + - To see your base URL, log in to the account. Once logged in, on all pages, you should see your base URL in this format: `xxxxx.api.infobip.com`. - Fill in the `From email address` field using the authenticated email from the previous step. - Fill in the `Sender's name`. - Click on the `Disabled` button and mark it as `Active`. - Click on the **Update** button. -- You should now be able to send notifications using Infobip in Novu. \ No newline at end of file +- You should now be able to send notifications using Infobip in Novu. diff --git a/channels-and-providers/email/mailersend.mdx b/integrations/providers/email/mailersend.mdx similarity index 69% rename from channels-and-providers/email/mailersend.mdx rename to integrations/providers/email/mailersend.mdx index c048ad81..7734e49d 100644 --- a/channels-and-providers/email/mailersend.mdx +++ b/integrations/providers/email/mailersend.mdx @@ -1,6 +1,6 @@ --- -title: 'Mailersend' -description: 'Learn how to use the MailerSend provider to send email notifications using Novu' +title: "Mailersend" +description: "Learn how to use the MailerSend provider to send email notifications using Novu" --- [MailerSend](https://www.mailersend.com/) is an email delivery service that allows you to send emails from your application. @@ -11,7 +11,7 @@ To use the MailerSend provider in the email channel, you will need to create a M ## Creating the MailerSend integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-mailersend) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-mailersend) page on Novu. - Click on Add a Provider. - Select MailerSend service. - Enter the API key. @@ -33,51 +33,52 @@ import { const novu = new Novu(''); await novu.subscribers.trigger("workflowIdentifier", { - to: "subscriberId", - payload: {}, - overrides: { - email: { - customData: { - // mailersend template templateId - templateId: 'mailersend-template-id', - // mailersend template variables - personalization: [{ - email: 'recipient@email.com', - data: { - items: { - price: '', - product: '', - quantity: '', - }, - order: { - date: '', - order_number: '', - billing_address: '', - customer_message: '', - }, - store: { - name: '', - }, - invoice: { - total: '', - subtotal: '', - pay_method: '', - }, - customer: { - name: '', - email: '', - phone: '', - }, - }, - }, ], - }, - } - }, - // actorId is subscriberId of actor - actor: "actorId" - tenant: "tenantIdentifier" +to: "subscriberId", +payload: {}, +overrides: { +email: { +customData: { +// mailersend template templateId +templateId: 'mailersend-template-id', +// mailersend template variables +personalization: [{ +email: 'recipient@email.com', +data: { +items: { +price: '', +product: '', +quantity: '', +}, +order: { +date: '', +order_number: '', +billing_address: '', +customer_message: '', +}, +store: { +name: '', +}, +invoice: { +total: '', +subtotal: '', +pay_method: '', +}, +customer: { +name: '', +email: '', +phone: '', +}, +}, +}, ], +}, +} +}, +// actorId is subscriberId of actor +actor: "actorId" +tenant: "tenantIdentifier" }); -``` + +```` ```bash @@ -126,7 +127,7 @@ curl --location 'https://api.novu.co/v1/events/trigger' \ } } }' -``` +```` + - diff --git a/channels-and-providers/email/mailgun.mdx b/integrations/providers/email/mailgun.mdx similarity index 77% rename from channels-and-providers/email/mailgun.mdx rename to integrations/providers/email/mailgun.mdx index c6fecce1..2039a07c 100644 --- a/channels-and-providers/email/mailgun.mdx +++ b/integrations/providers/email/mailgun.mdx @@ -1,6 +1,6 @@ --- -title: 'Mailgun' -description: 'Learn how to use the Mailgun provider to send email notifications using Novu' +title: "Mailgun" +description: "Learn how to use the Mailgun provider to send email notifications using Novu" --- You can use the [Mailgun](https://mailgun.com/) provider to send transactional emails to your customers using the Novu Platform with a single API to create multi-channel experiences. @@ -22,22 +22,22 @@ To generate a new API key in Mailgun, you can follow these steps: Mailgun recommends that you add a subdomain as a domain name. To do so: - Visit the page to add a [domain name](https://app.mailgun.com/app/sending/domains/new). - - During this process, you will need to choose a region for the domain name which is between `US` and `EU`. The default is `US`. + - During this process, you will need to choose a region for the domain name which is between `US` and `EU`. The default is `US`. - Follow the [instructions](https://documentation.mailgun.com/en/latest/user_manual.html#verifying-your-domain-1) to verify the domain name. # Creating a Mailgun integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-mailgun) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-mailgun) page on Novu. - Click on Add a Provider. - Select Mailgun service. - Enter your Mailgun API Key. - Enter your Base URL. - - For domains created in the EU region, the base URL is: `https://api.eu.mailgun.net/` - - Otherwise, leave the base URL blank. + - For domains created in the EU region, the base URL is: `https://api.eu.mailgun.net/` + - Otherwise, leave the base URL blank. - Fill in the `Username`. - Fill in the `Domain name` registered on Mailgun. - Fill in the `From email address` field using the authenticated email from the previous step. - Fill in the `Sender's name`. - Click on the `Disabled` button and mark it as `Active`. - Click on the **Update** button. -- You should now be able to send notifications using Mailgun in Novu. \ No newline at end of file +- You should now be able to send notifications using Mailgun in Novu. diff --git a/channels-and-providers/email/mailjet.mdx b/integrations/providers/email/mailjet.mdx similarity index 90% rename from channels-and-providers/email/mailjet.mdx rename to integrations/providers/email/mailjet.mdx index 58c88d6e..2c15a310 100644 --- a/channels-and-providers/email/mailjet.mdx +++ b/integrations/providers/email/mailjet.mdx @@ -1,6 +1,6 @@ --- -title: 'Mailjet' -description: 'Learn how to use the Mailjet provider to send email notifications using Novu' +title: "Mailjet" +description: "Learn how to use the Mailjet provider to send email notifications using Novu" --- You can use the [Mailjet](https://mailjet.com/) provider to send transactional emails to your customers using the Novu Platform with a single API. @@ -30,11 +30,11 @@ Mailjet allows you to authenticate your sender identity using one of the followi # Creating a Mailjet integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-mailjet) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-mailjet) page on Novu. - Click on Add a Provider. - Select Mailjet service. - Enter your Mailjet API key. - Fill in the `From email address` field using the authenticated email from the previous step. - Click on the `Disabled` button and mark it as `Active`. - Click on the **Update** button. -- You should now be able to send notifications using Mailjet in Novu. \ No newline at end of file +- You should now be able to send notifications using Mailjet in Novu. diff --git a/channels-and-providers/email/mailtrap.mdx b/integrations/providers/email/mailtrap.mdx similarity index 86% rename from channels-and-providers/email/mailtrap.mdx rename to integrations/providers/email/mailtrap.mdx index 307353db..b20f677e 100644 --- a/channels-and-providers/email/mailtrap.mdx +++ b/integrations/providers/email/mailtrap.mdx @@ -1,6 +1,6 @@ --- -title: 'Mailtrap' -description: 'Learn how to use the Mailtrap provider to send email notifications using Novu' +title: "Mailtrap" +description: "Learn how to use the Mailtrap provider to send email notifications using Novu" --- You can use the [Mailtrap](https://mailtrap.io/email-sending/) provider to send transactional emails to your customers using the Novu Platform with a single API to create multi-channel experiences. @@ -15,7 +15,7 @@ To generate a new API key in Mailtrap, you can follow these steps: - [Sign Up](https://mailtrap.io/register/signup) or [Log in](https://mailtrap.io/signin) to your Mailtrap account. - Click on the **Email Sending** link on the sidebar, and then click the "Sending Domains" link that pops up from the available options. -- On the [Sending Domains](https://mailtrap.io/sending/domains) page, type your domain name and confirm with the `Add Your Domain` button. Then, proceed to copy DNS records Mailtrap provides to your domain’s DNS. +- On the [Sending Domains](https://mailtrap.io/sending/domains) page, type your domain name and confirm with the `Add Your Domain` button. Then, proceed to copy DNS records Mailtrap provides to your domain’s DNS. - Go to [API Keys](https://mailtrap.io/api-tokens) page and copy token with `Domain Admin` access level for your registered domain. # Authenticating your Sender Identity @@ -24,7 +24,7 @@ Before you can send emails, you will need to [verify your sending domain ownersh # Creating a Mailtrap integration with Novu -- Visit the [Integrations store](https://web.novu.co/integrations?utm_campaign=docs-mailtrap) on the Novu web dashboard. +- Visit the [Integrations store](https://dashboard.novu.co/integrations?utm_campaign=docs-mailtrap) on the Novu web dashboard. - Click on Add a Provider. - Select Mailtrap service. - Enter your Mailtrap API Key. diff --git a/channels-and-providers/email/mandrill.mdx b/integrations/providers/email/mandrill.mdx similarity index 89% rename from channels-and-providers/email/mandrill.mdx rename to integrations/providers/email/mandrill.mdx index bb7c2b8f..d449413d 100644 --- a/channels-and-providers/email/mandrill.mdx +++ b/integrations/providers/email/mandrill.mdx @@ -1,6 +1,6 @@ --- -title: 'Mandrill' -description: 'Learn how to use the Mandrill provider to send email notifications using Novu' +title: "Mandrill" +description: "Learn how to use the Mandrill provider to send email notifications using Novu" --- You can use the [Mandrill by Mailchimp](https://mandrillapp.com/) provider to send transactional emails to your customers using the Novu Platform with a single API. @@ -27,7 +27,7 @@ To get started, you’ll need to add the domain that you want to send messages f # Creating the Mandrill integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-mandrill) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-mandrill) page on Novu. - Click on Add a Provider. - Select Mandrill service. - Enter your Mandrill API key. @@ -35,4 +35,4 @@ To get started, you’ll need to add the domain that you want to send messages f - Fill in the `Sender's name`. - Click on the `Disabled` button and mark it as `Active`. - Click on the **Update** button. -- You should now be able to send notifications using Mandrill in Novu. \ No newline at end of file +- You should now be able to send notifications using Mandrill in Novu. diff --git a/channels-and-providers/email/maqsam.mdx b/integrations/providers/email/maqsam.mdx similarity index 81% rename from channels-and-providers/email/maqsam.mdx rename to integrations/providers/email/maqsam.mdx index f5344935..b62dc639 100644 --- a/channels-and-providers/email/maqsam.mdx +++ b/integrations/providers/email/maqsam.mdx @@ -1,11 +1,10 @@ --- -title: 'Maqsam' -description: 'Learn how to use the Maqsam provider to send sms notifications using Novu' +title: "Maqsam" +description: "Learn how to use the Maqsam provider to send sms notifications using Novu" --- You can use the [Maqsam](https://maqsam.com/) provider to send SMS messages to your customers using the Novu platform with a single API to create multi-channel experiences. - # Getting Started To use the Maqsam provider in the sms channel, you will need to create a Maqsam account and add your access key & secret to the Maqsam integration on the Novu platform. @@ -13,11 +12,11 @@ Contact `support@maqsam.com` to enable the API feature for your account, then yo # Creating the Maqsam integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-maqsam) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-maqsam) page on Novu. - Click the "Add a provider" button. - Select Maqsam service - Click `Next` - Choose your preferred deployment environment: `Development` or `Production`. Then Click the `Create` button. - Click on the `Disabled` button and mark it as `Active`. - Click on the **Update** button. -- You should now be able to send notifications using Maqsam in Novu. \ No newline at end of file +- You should now be able to send notifications using Maqsam in Novu. diff --git a/channels-and-providers/email/netcore.mdx b/integrations/providers/email/netcore.mdx similarity index 88% rename from channels-and-providers/email/netcore.mdx rename to integrations/providers/email/netcore.mdx index 4dab1c45..352953c4 100644 --- a/channels-and-providers/email/netcore.mdx +++ b/integrations/providers/email/netcore.mdx @@ -1,6 +1,6 @@ --- -title: 'Netcore' -description: 'Learn how to use the Netcore provider to send email notifications using Novu' +title: "Netcore" +description: "Learn how to use the Netcore provider to send email notifications using Novu" --- You can use the [Netcore](https://netcorecloud.com/) provider to send transactional emails to your customers using the Novu Platform with a single API to create multi-channel experiences. @@ -25,7 +25,7 @@ Follow the instructions on this [page](https://emaildocs.netcorecloud.com/docs/ # Creating a Netcore integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-netcore) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-netcore) page on Novu. - Click on Add a Provider. - Select Netcore service. - Enter your Netcore API Key. @@ -33,4 +33,4 @@ Follow the instructions on this [page](https://emaildocs.netcorecloud.com/docs/ - Fill in the `Sender's name`. - Click on the `Disabled` button and mark it as `Active`. - Click on the **Update** button. -- You should now be able to send notifications using Netcore in Novu. \ No newline at end of file +- You should now be able to send notifications using Netcore in Novu. diff --git a/channels-and-providers/email/outlook365.mdx b/integrations/providers/email/outlook365.mdx similarity index 53% rename from channels-and-providers/email/outlook365.mdx rename to integrations/providers/email/outlook365.mdx index 38962d41..52e5e7a1 100644 --- a/channels-and-providers/email/outlook365.mdx +++ b/integrations/providers/email/outlook365.mdx @@ -1,6 +1,6 @@ --- -title: 'Outlook 365' -description: 'Learn how to use the Outlook 365 provider to send email notifications using Novu' +title: "Outlook 365" +description: "Learn how to use the Outlook 365 provider to send email notifications using Novu" --- You can use the [Outlook 365](https://office.com/) provider to send transactional emails through your instance of Office 365 to your customers using the Novu Platform with a single API. @@ -11,15 +11,19 @@ To use the Outlook 365 provider in the email channel, you will need to have the ## Creating the Outlook 365 integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-office365) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-office365) page on Novu. - Click on Add a Provider. - Select Outlook service. - Enter your SMTP credentials - - `from`: The Complete email address of the sending user. (e.g. [jdoe@mycompany.com](mailto:jdoe@mycompany.com)) - - `senderName`: Sender Name should be the same email address of the sending user. (e.g. [jdoe@mycompany.com](mailto:jdoe@mycompany.com)) - - `password`: Password used to sign in with the email account. + - `from`: The Complete email address of the sending user. (e.g. [jdoe@mycompany.com](mailto:jdoe@mycompany.com)) + - `senderName`: Sender Name should be the same email address of the sending user. (e.g. [jdoe@mycompany.com](mailto:jdoe@mycompany.com)) + - `password`: Password used to sign in with the email account. - Click on the `Disabled` button and mark it as `Active`. - Click on the **Update** button. - You should now be able to send notifications using Outlook 365 in Novu. - In order to create outlook integration, turn off multi factor authentication from account security settings. \ No newline at end of file + + {" "} + In order to create outlook integration, turn off multi factor authentication from + account security settings. + diff --git a/channels-and-providers/email/overview.mdx b/integrations/providers/email/overview.mdx similarity index 73% rename from channels-and-providers/email/overview.mdx rename to integrations/providers/email/overview.mdx index 90dbd4da..70130abd 100644 --- a/channels-and-providers/email/overview.mdx +++ b/integrations/providers/email/overview.mdx @@ -1,13 +1,20 @@ --- -title: 'Email providers' -description: 'Learn the process of configuring email providers with Novu' +title: "E-mail Provider Integrations" +sidebarTitle: "Overview" +description: "Learn the process of configuring email providers with Novu" --- import { MissingProvider } from "/snippets/missing-provider.mdx"; Novu can be used to deliver email messages to your customers using a unified delivery API. You can easily integrate your favorite email provider using the built-in integration store. - + ## Configuring email providers @@ -30,22 +37,23 @@ import { Novu } from '@novu/node'; const novu = new Novu(''); novu.trigger('', { - to: { - subscriberId: '', - }, - overrides: { - email: { - to: ['to@novu.co'], - from: 'from@novu.co', - senderName: 'Novu Team', - text: 'text version of email using overrides', - replyTo: 'no-reply@novu.co', - cc: ['1@novu.co'], - bcc: ['2@novu.co'], - }, - }, +to: { +subscriberId: '', +}, +overrides: { +email: { +to: ['to@novu.co'], +from: 'from@novu.co', +senderName: 'Novu Team', +text: 'text version of email using overrides', +replyTo: 'no-reply@novu.co', +cc: ['1@novu.co'], +bcc: ['2@novu.co'], +}, +}, }); -``` + +```` @@ -77,15 +85,17 @@ novu.trigger('', { ], }, }); -``` +```` + ## Using different email integration -In Novu integration store, multiple email channel type provider integrations can be active at the same time. But only one provider integration can be primary at a time. This primary integration will be used as a provider to deliver the email by default. If you want to use a different active provider integration then you can use the `integrationIdentifier` email overrides field. +In Novu integration store, multiple email channel type provider integrations can be active at the same time. But only one provider integration can be primary at a time. This primary integration will be used as a provider to deliver the email by default. If you want to use a different active provider integration then you can use the `integrationIdentifier` email overrides field. + +If there are 4 active email integrations with these identifiers:- -If there are 4 active email integrations with these identifiers:- 1. sendgrid-abcdef 2. sendgrid-ghijkl 3. brevo-abcdef @@ -101,15 +111,16 @@ import { Novu } from '@novu/node'; const novu = new Novu(''); novu.trigger('', { - to: { - subscriberId: '', - }, - overrides: { - email: { - integrationIdentifier: "brevo-abcdef" - }, - }, +to: { +subscriberId: '', +}, +overrides: { +email: { +integrationIdentifier: "brevo-abcdef" +}, +}, }); + ``` @@ -118,4 +129,5 @@ novu.trigger('', { - \ No newline at end of file + +``` diff --git a/channels-and-providers/email/plunk.mdx b/integrations/providers/email/plunk.mdx similarity index 85% rename from channels-and-providers/email/plunk.mdx rename to integrations/providers/email/plunk.mdx index 92639710..4217e25f 100644 --- a/channels-and-providers/email/plunk.mdx +++ b/integrations/providers/email/plunk.mdx @@ -1,6 +1,6 @@ --- -title: 'Plunk' -description: 'Learn how to use the Plunk provider to send email notifications using Novu' +title: "Plunk" +description: "Learn how to use the Plunk provider to send email notifications using Novu" --- You can use the [Plunk](https://useplunk.com/) provider to send transactional emails to your customers using the Novu Platform with a single API to create multi-channel experiences. @@ -23,11 +23,11 @@ Before you can send emails on a large scale, you will need to authenticate your ## Create a Plunk integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-plunk) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-plunk) page on Novu. - Click on Add a Provider. - Select Plunk service. - Enter your Plunk secret API Key. - Fill the `From email address` field using the authenticated email from the previous step. - Click on the `Disabled` button and mark as `Active`. - Click on the **Update** button. -- You should now be able to send notifications using Plunk in Novu. \ No newline at end of file +- You should now be able to send notifications using Plunk in Novu. diff --git a/channels-and-providers/email/postmark.mdx b/integrations/providers/email/postmark.mdx similarity index 89% rename from channels-and-providers/email/postmark.mdx rename to integrations/providers/email/postmark.mdx index afbb7a65..bbf458c4 100644 --- a/channels-and-providers/email/postmark.mdx +++ b/integrations/providers/email/postmark.mdx @@ -1,6 +1,6 @@ --- -title: 'Postmark' -description: 'Learn how to use the Postmark provider to send email notifications using Novu' +title: "Postmark" +description: "Learn how to use the Postmark provider to send email notifications using Novu" --- It is possible to use [Postmark](https://postmarkapp.com/) as a provider to send transactional emails to your customers using the Novu Platform with a single API. @@ -25,7 +25,7 @@ Postmark allows the authentication of the sender's identity using one of the fol # Create a Postmark integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-postmark) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-postmark) page on Novu. - Click on Add a Provider. - Select Postmark service. - Enter the Postmark API key. diff --git a/channels-and-providers/email/resend.mdx b/integrations/providers/email/resend.mdx similarity index 82% rename from channels-and-providers/email/resend.mdx rename to integrations/providers/email/resend.mdx index 3595b2d0..5f5c3c97 100644 --- a/channels-and-providers/email/resend.mdx +++ b/integrations/providers/email/resend.mdx @@ -1,6 +1,6 @@ --- -title: 'Resend' -description: 'Learn how to use the Resend provider to send email notifications using Novu' +title: "Resend" +description: "Learn how to use the Resend provider to send email notifications using Novu" --- You can use the [Resend](https://resend.com/) provider to send transactional emails to your customers using the Novu Platform with a single API to create multi-channel experiences. @@ -27,13 +27,13 @@ Resend allows you to authenticate your sender identity using [Domain Authentica # Creating a Resend integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-resend) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-resend) page on Novu. - Click on Add a Provider. - Select Resend service. - Enter your Resend API Key. - Fill in the `From email address` field using the authenticated email from the previous step. - - For testing, you can use `onboarding@resend.dev` if you have not authenticated your sender identity. + - For testing, you can use `onboarding@resend.dev` if you have not authenticated your sender identity. - Fill in the `Sender's name`. - Click on the `Disabled` button and mark it as `Active`. - Click on the **Update** button. -- You should now be able to send notifications using Resend in Novu. \ No newline at end of file +- You should now be able to send notifications using Resend in Novu. diff --git a/channels-and-providers/email/sendgrid.mdx b/integrations/providers/email/sendgrid.mdx similarity index 77% rename from channels-and-providers/email/sendgrid.mdx rename to integrations/providers/email/sendgrid.mdx index 80f919f5..f7eab2fb 100644 --- a/channels-and-providers/email/sendgrid.mdx +++ b/integrations/providers/email/sendgrid.mdx @@ -39,7 +39,7 @@ SendGrid allows you to authenticate your sender identity using one of the follow ## SendGrid integration with Novu -- Visit the [Integrations store](https://web.novu.co/integrations?utm_campaign=docs-sendgrid) on the Novu web dashboard. +- Visit the [Integrations store](https://dashboard.novu.co/integrations?utm_campaign=docs-sendgrid) on the Novu web dashboard. - Click on Add a Provider. - Select SendGrid service. - Enter your SendGrid API Key. @@ -62,43 +62,44 @@ import { Novu } from '@novu/node'; const novu = new Novu(''); await novu.subscribers.trigger("workflowIdentifier", { - to: "subscriberId", - payload: {}, - overrides: { - email: { - customData: { - // sendgrid template templateId - templateId: 'sendgrid-template-id', - // sendgrid template variables - dynamicTemplateData: { - total: '$ 239.85', - items: [{ - text: 'New Line Sneakers', - image: 'https://marketing-image-production.s3.amazonaws.com/uploads/8dda1131320a6d978b515cc04ed479df259a458d5d45d58b6b381cae0bf9588113e80ef912f69e8c4cc1ef1a0297e8eefdb7b270064cc046b79a44e21b811802.png', - price: '$ 79.95', - }, - { - text: 'Old Line Sneakers rlfjrjrh4hr4rh4', - image: 'https://marketing-image-production.s3.amazonaws.com/uploads/3629f54390ead663d4eb7c53702e492de63299d7c5f7239efdc693b09b9b28c82c924225dcd8dcb65732d5ca7b7b753c5f17e056405bbd4596e4e63a96ae5018.png', - price: '$ 79.95', - }, - ], - receipt: true, - name: 'Sample Name', - address01: '1234 Fake St.', - address02: 'Apt. 123', - city: 'Place', - state: 'CO', - zip: '80202', - }, - }, - } - }, - // actorId is subscriberId of actor - actor: "actorId" - tenant: "tenantIdentifier" +to: "subscriberId", +payload: {}, +overrides: { +email: { +customData: { +// sendgrid template templateId +templateId: 'sendgrid-template-id', +// sendgrid template variables +dynamicTemplateData: { +total: '$ 239.85', +items: [{ +text: 'New Line Sneakers', +image: 'https://marketing-image-production.s3.amazonaws.com/uploads/8dda1131320a6d978b515cc04ed479df259a458d5d45d58b6b381cae0bf9588113e80ef912f69e8c4cc1ef1a0297e8eefdb7b270064cc046b79a44e21b811802.png', +price: '$ 79.95', +}, +{ +text: 'Old Line Sneakers rlfjrjrh4hr4rh4', +image: 'https://marketing-image-production.s3.amazonaws.com/uploads/3629f54390ead663d4eb7c53702e492de63299d7c5f7239efdc693b09b9b28c82c924225dcd8dcb65732d5ca7b7b753c5f17e056405bbd4596e4e63a96ae5018.png', +price: '$ 79.95', +}, +], +receipt: true, +name: 'Sample Name', +address01: '1234 Fake St.', +address02: 'Apt. 123', +city: 'Place', +state: 'CO', +zip: '80202', +}, +}, +} +}, +// actorId is subscriberId of actor +actor: "actorId" +tenant: "tenantIdentifier" }); -``` + +```` ```bash @@ -140,6 +141,7 @@ curl --location 'https://api.novu.co/v1/events/trigger' \ } } }' -``` +```` + - \ No newline at end of file + diff --git a/channels-and-providers/email/sendinblue.mdx b/integrations/providers/email/sendinblue.mdx similarity index 72% rename from channels-and-providers/email/sendinblue.mdx rename to integrations/providers/email/sendinblue.mdx index ce0c446a..4ed10ec4 100644 --- a/channels-and-providers/email/sendinblue.mdx +++ b/integrations/providers/email/sendinblue.mdx @@ -1,6 +1,6 @@ --- -title: 'Sendinblue' -description: 'Learn how to use the Sendinblue provider to send email notifications using Novu' +title: "Sendinblue" +description: "Learn how to use the Sendinblue provider to send email notifications using Novu" --- You can use the [Sendinblue](https://www.sendinblue.com/) provider to send transactional emails to your customers using the Novu Platform with a single API. @@ -24,7 +24,7 @@ Sendinblue allows you to authenticate your sender identity using one of the foll # Creating a Sendinblue integration with Novu -- Visit the [Integrations store](https://web.novu.co/integrations?utm_campaign=docs-sendinblue) on the Novu web dashboard. +- Visit the [Integrations store](https://dashboard.novu.co/integrations?utm_campaign=docs-sendinblue) on the Novu web dashboard. - Click on Add a Provider. - Select Sendinblue service. - Enter your Sendinblue API key. @@ -47,43 +47,44 @@ import { Novu } from '@novu/node'; const novu = new Novu(''); await novu.subscribers.trigger("workflowIdentifier", { - to: "subscriberId", - payload: {}, - overrides: { - email: { - customData: { - // sendinblue template templateId - templateId: 1, - // sendinblue template variables - templateParams: { - total: '$ 239.85', - items: [{ - text: 'New Line Sneakers', - image: 'https://marketing-image-production.s3.amazonaws.com/uploads/8dda1131320a6d978b515cc04ed479df259a458d5d45d58b6b381cae0bf9588113e80ef912f69e8c4cc1ef1a0297e8eefdb7b270064cc046b79a44e21b811802.png', - price: '$ 79.95', - }, - { - text: 'Old Line Sneakers rlfjrjrh4hr4rh4', - image: 'https://marketing-image-production.s3.amazonaws.com/uploads/3629f54390ead663d4eb7c53702e492de63299d7c5f7239efdc693b09b9b28c82c924225dcd8dcb65732d5ca7b7b753c5f17e056405bbd4596e4e63a96ae5018.png', - price: '$ 79.95', - }, - ], - receipt: true, - name: 'Sample Name', - address01: '1234 Fake St.', - address02: 'Apt. 123', - city: 'Place', - state: 'CO', - zip: '80202', - }, - }, - } - }, - // actorId is subscriberId of actor - actor: "actorId", - tenant: "tenantIdentifier" +to: "subscriberId", +payload: {}, +overrides: { +email: { +customData: { +// sendinblue template templateId +templateId: 1, +// sendinblue template variables +templateParams: { +total: '$ 239.85', +items: [{ +text: 'New Line Sneakers', +image: 'https://marketing-image-production.s3.amazonaws.com/uploads/8dda1131320a6d978b515cc04ed479df259a458d5d45d58b6b381cae0bf9588113e80ef912f69e8c4cc1ef1a0297e8eefdb7b270064cc046b79a44e21b811802.png', +price: '$ 79.95', +}, +{ +text: 'Old Line Sneakers rlfjrjrh4hr4rh4', +image: 'https://marketing-image-production.s3.amazonaws.com/uploads/3629f54390ead663d4eb7c53702e492de63299d7c5f7239efdc693b09b9b28c82c924225dcd8dcb65732d5ca7b7b753c5f17e056405bbd4596e4e63a96ae5018.png', +price: '$ 79.95', +}, +], +receipt: true, +name: 'Sample Name', +address01: '1234 Fake St.', +address02: 'Apt. 123', +city: 'Place', +state: 'CO', +zip: '80202', +}, +}, +} +}, +// actorId is subscriberId of actor +actor: "actorId", +tenant: "tenantIdentifier" }); -``` + +```` ```bash @@ -125,6 +126,7 @@ curl --location 'https://api.novu.co/v1/events/trigger' \ } } }' -``` +```` + - \ No newline at end of file + diff --git a/channels-and-providers/email/sparkpost.mdx b/integrations/providers/email/sparkpost.mdx similarity index 70% rename from channels-and-providers/email/sparkpost.mdx rename to integrations/providers/email/sparkpost.mdx index 034429db..28975494 100644 --- a/channels-and-providers/email/sparkpost.mdx +++ b/integrations/providers/email/sparkpost.mdx @@ -1,6 +1,6 @@ --- -title: 'Sparkpost' -description: 'Learn how to use the Sparkpost provider to send email notifications using Novu' +title: "Sparkpost" +description: "Learn how to use the Sparkpost provider to send email notifications using Novu" --- You can use the [SparkPost](https://messagebird.com/email/cloud-sending) provider to send transactional emails to your customers using the Novu Platform with a single API. @@ -14,12 +14,9 @@ To use the SparkPost provider in the email channel, you will need to create a Sp To generate a new API key in SparkPost, you can follow these steps: - [Sign up](https://app.sparkpost.com/join) or [Log in](https://app.sparkpost.com/auth) to your SparkPost account. - - > During sign up, note that SparkPost is available in multiple regions. "SparkPost" refers to the SparkPost service hosted in North America. "SparkPost EU" refers to the SparkPost service hosted in Western Europe. An account created with SparkPost cannot be used with SparkPost EU, and vice-versa. You may use accounts in both regions. - > - > - > ~ *[SparkPost Documentation](https://support.sparkpost.com/docs/getting-started/getting-started-sparkpost/)* - > + > During sign up, note that SparkPost is available in multiple regions. "SparkPost" refers to the SparkPost service hosted in North America. "SparkPost EU" refers to the SparkPost service hosted in Western Europe. An account created with SparkPost cannot be used with SparkPost EU, and vice-versa. You may use accounts in both regions. + > + > ~ *[SparkPost Documentation](https://support.sparkpost.com/docs/getting-started/getting-started-sparkpost/)* - Click on the **Configuration** link on the navbar, and then click the "API Keys" link that pops up from the available options. - On the [API Keys](https://app.sparkpost.com/account/api-keys) page, click the **Create API Key** button. - Give the API key a name and click on the **Create API key** button. @@ -33,7 +30,7 @@ SparkPost allows you to authenticate your sender identity using [Sending Domain # Creating a SparkPost integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-sparkpost) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-sparkpost) page on Novu. - Click on Add a Provider. - Select SparkPost service. - Enter your SparkPost API Key. @@ -42,4 +39,4 @@ SparkPost allows you to authenticate your sender identity using [Sending Domain - Toggle the `eu` switch to true if you're in Western Europe - Click on the `Disabled` button and mark it as `Active`. - Click on the **Update** button. -- You should now be able to send notifications using SparkPost in Novu. \ No newline at end of file +- You should now be able to send notifications using SparkPost in Novu. diff --git a/channels-and-providers/email/webhook.mdx b/integrations/providers/email/webhook.mdx similarity index 68% rename from channels-and-providers/email/webhook.mdx rename to integrations/providers/email/webhook.mdx index 466889eb..bb626f04 100644 --- a/channels-and-providers/email/webhook.mdx +++ b/integrations/providers/email/webhook.mdx @@ -1,17 +1,17 @@ --- -title: 'Email Webhook' -description: 'Learn how to use the Email Webhook provider to send email notifications using Novu' +title: "Email Webhook" +description: "Learn how to use the Email Webhook provider to send email notifications using Novu" --- [Email Webhooks](https://www.socketlabs.com/blog/what-is-a-webhook/#:~:text=Email%20webhooks%20are%20an%20extremely,%2C%20successful%20messages%2C%20and%20bounces.) are an extremely flexible way for developers to monitor the health of their mail stream in real time # Getting Started -First go to [Email Settings](https://web.novu.co/settings/email?utm_campaign=docs-webhook) in Settings menu to configure Email Webhook. +First go to [Email Settings](https://dashboard.novu.co/settings/email?utm_campaign=docs-webhook) in Settings menu to configure Email Webhook. # Creating the Email Webhook integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-webhook) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-webhook) page on Novu. - Click the "Add a provider" button. - Select Email Webhook service - Click `Next` @@ -20,4 +20,4 @@ First go to [Email Settings](https://web.novu.co/settings/email?utm_campaign=doc - Finally add `From email address` & `Sender name` to identify email sender - Click on the `Disabled` button and mark it as `Active`. - Click on the **Update** button. -- You should now be able to send notifications using Email Webhook in Novu. \ No newline at end of file +- You should now be able to send notifications using Email Webhook in Novu. diff --git a/channels-and-providers/in-app/introduction.mdx b/integrations/providers/in-app/introduction.mdx similarity index 90% rename from channels-and-providers/in-app/introduction.mdx rename to integrations/providers/in-app/introduction.mdx index 54ad8ed1..e11bd23d 100644 --- a/channels-and-providers/in-app/introduction.mdx +++ b/integrations/providers/in-app/introduction.mdx @@ -1,7 +1,9 @@ --- -title: 'Introduction' -description: 'Learn how to integrate In-App notification center and send In-App notification using Novu' +title: "Inbox" +description: "Learn how to integrate In-App notification center and send In-App notification using Novu" +icon: "bell" --- + In-app notifications are messages or alerts that appear within a mobile app or website while a user is actively using it. Unlike push notifications, which can reach users even when they're not actively engaged with an app, in-app notifications are designed to enhance the user experience within the app itself. Here's why they are important: 1. User Engagement: In-app notifications can keep users engaged and informed while they are using your app, providing valuable updates, reminders, or information relevant to their current activity. This can improve user satisfaction and encourage them to spend more time within the app. @@ -20,14 +22,14 @@ In-app notifications are messages or alerts that appear within a mobile app or w 8. A/B Testing and Optimization: In-app notifications can be A/B tested to determine which messages and designs are most effective in achieving specific goals, such as increasing conversions or user engagement. - ## In-app notifications using Novu - + Novu provides a pre built component to display in-app notifications for almost all web frameworks. You can use this component to display in-app notifications in your web app. If you don't want to use the pre built component, you can use the headless component to build your own in-app notification center. Read more about notification center [here](/notification-center/introduction). - - - - - diff --git a/integrations/providers/introduction.mdx b/integrations/providers/introduction.mdx new file mode 100644 index 00000000..048c6b4c --- /dev/null +++ b/integrations/providers/introduction.mdx @@ -0,0 +1,18 @@ +--- +title: "Overview" +description: "Learn about what channels, providers and oganizations are" +--- + +## Channels + +Novu lets you send notifications across different communication mediums, including emails, in-app messages, push notifications, SMS, and chat. Each of these five communication mediums is referred to as a notification ‘channel’. + + + {" "} + + +## Providers + +Providers are what allow us to handle message delivery over multiple channels. Of the five notification channels that Novu currently supports, each channel except the ‘in-app’ channel has multiple providers. + +The Inbox channel only has one provider (the default Novu provider). All the remaining channels have multiple providers. diff --git a/channels-and-providers/push/apns.mdx b/integrations/providers/push/apns.mdx similarity index 76% rename from channels-and-providers/push/apns.mdx rename to integrations/providers/push/apns.mdx index d5d25edd..501bdcfe 100644 --- a/channels-and-providers/push/apns.mdx +++ b/integrations/providers/push/apns.mdx @@ -1,7 +1,7 @@ --- -title: 'Apple Push Notification Service (APNS)' -sidebarTitle: 'APNS' -description: 'Learn how to use the Apple Push Notification Service (APNS) provider to send push notifications using Novu' +title: "Apple Push Notification Service (APNS)" +sidebarTitle: "APNS" +description: "Learn how to use the Apple Push Notification Service (APNS) provider to send push notifications using Novu" --- [Apple Push Notification Service](https://docs.expo.dev/push-notifications/overview/), as the name suggests, is a notification delivery service provided by Apple. @@ -27,27 +27,27 @@ You also need the following to connect to APNs: The overrides field supports all [Notification payload](https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/generating_a_remote_notification?language=objc) values, as shown below: ```jsx -import { Novu } from '@novu/node'; +import { Novu } from "@novu/node"; const novu = new Novu(""); -novu.trigger('', { +novu.trigger("", { to: { - subscriberId: '', + subscriberId: "", }, payload: { - abc: 'def', // If the notification is a data notification, the payload will be sent as the data + abc: "def", // If the notification is a data notification, the payload will be sent as the data }, overrides: { apns: { payload: { aps: { notification: { - title: 'Test', - body: 'Test push', + title: "Test", + body: "Test push", }, data: { - key: 'value', + key: "value", }, }, }, @@ -57,31 +57,31 @@ novu.trigger('', { ``` ```jsx -import { Novu } from '@novu/node'; +import { Novu } from "@novu/node"; const novu = new Novu(""); -novu.trigger('', { +novu.trigger("", { to: { - subscriberId: '', + subscriberId: "", }, payload: { - key1: 'val1', - key2: 'val2', // If the notification is a data notification, the payload will be sent as the data + key1: "val1", + key2: "val2", // If the notification is a data notification, the payload will be sent as the data }, overrides: { - type: 'data', + type: "data", apns: { headers: { - 'apns-priority': '5', + "apns-priority": "5", }, payload: { aps: { alert: { - 'loc-key': 'GAME_PLAY_REQUEST_FORMAT', - 'loc-args': ['Shelly', 'Rick'], + "loc-key": "GAME_PLAY_REQUEST_FORMAT", + "loc-args": ["Shelly", "Rick"], }, - sound: 'demo.wav', + sound: "demo.wav", }, }, }, @@ -102,12 +102,13 @@ import { const novu = new Novu(""); await novu.subscribers.setCredentials('subscriberId', PushProviderIdEnum.APNS, { - deviceTokens: ['token1', 'token2'], +deviceTokens: ['token1', 'token2'], }); -``` + +```` -```bash +```bash curl -L -X PUT 'https://api.novu.co/v1/subscribers//credentials' \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ @@ -117,8 +118,9 @@ curl -L -X PUT 'https://api.novu.co/v1/subscribers//credentials' "deviceTokens": ["token1", "token2"], "integrationIdentifier": "apns-MnGLxp8uy" }' -``` +```` + -Checkout the [API reference](/api-reference/subscribers/update-subscriber-credentials) for more details. \ No newline at end of file +Checkout the [API reference](/api-reference/subscribers/update-subscriber-credentials) for more details. diff --git a/channels-and-providers/push/expo-push.mdx b/integrations/providers/push/expo-push.mdx similarity index 82% rename from channels-and-providers/push/expo-push.mdx rename to integrations/providers/push/expo-push.mdx index a5536d80..e7aebd33 100644 --- a/channels-and-providers/push/expo-push.mdx +++ b/integrations/providers/push/expo-push.mdx @@ -1,6 +1,6 @@ --- -title: 'Expo Push' -description: 'Learn how to use the Expo push provider to send push notifications using Novu' +title: "Expo Push" +description: "Learn how to use the Expo push provider to send push notifications using Novu" --- [Expo Push](https://docs.expo.dev/push-notifications/overview/) is a notification delivery service provided by Expo. @@ -10,16 +10,16 @@ To enable Expo Push integration, you need to create an [Expo Application Servic The overrides field supports all [Message Request](https://docs.expo.dev/push-notifications/sending-notifications/#message-request-format) values. An example of the same follows: ```jsx -import { Novu } from '@novu/node'; +import { Novu } from "@novu/node"; const novu = new Novu(""); -novu.trigger('', { +novu.trigger("", { to: { - subscriberId: '', + subscriberId: "", }, payload: { - abc: 'def', + abc: "def", }, }); ``` @@ -36,12 +36,13 @@ import { const novu = new Novu(""); await novu.subscribers.setCredentials('subscriberId', PushProviderIdEnum.EXPO, { - deviceTokens: ['token1', 'token2'], +deviceTokens: ['token1', 'token2'], }); -``` + +```` -```bash +```bash curl -L -X PUT 'https://api.novu.co/v1/subscribers//credentials' \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ @@ -51,7 +52,8 @@ curl -L -X PUT 'https://api.novu.co/v1/subscribers//credentials' "deviceTokens": ["token1", "token2"], "integrationIdentifier": "expo-MnGLxp8uy" }' -``` +```` + diff --git a/channels-and-providers/push/fcm.mdx b/integrations/providers/push/fcm.mdx similarity index 98% rename from channels-and-providers/push/fcm.mdx rename to integrations/providers/push/fcm.mdx index a81b1fc7..2b2c6c10 100644 --- a/channels-and-providers/push/fcm.mdx +++ b/integrations/providers/push/fcm.mdx @@ -63,12 +63,13 @@ import { const novu = new Novu(""); await novu.subscribers.setCredentials( - 'subscriberId', - PushProviderIdEnum.FCM, - { deviceTokens: ['token1', 'token2'] }, - 'fcm-MnGLxp8uy' +'subscriberId', +PushProviderIdEnum.FCM, +{ deviceTokens: ['token1', 'token2'] }, +'fcm-MnGLxp8uy' ); -``` + +```` @@ -81,13 +82,14 @@ curl -L -X PUT 'https://api.novu.co/v1/subscribers//credentials' "providerId": "fcm", "credentials": { "deviceTokens" : [ - "token1", + "token1", "token2" ] }, "integrationIdentifier": "fcm-MnGLxp8uy" }' -``` +```` + @@ -192,7 +194,8 @@ novu.trigger("", { -## FCM Cost +## FCM Cost + As per Firebase [pricing](https://firebase.google.com/pricing), **Cloud Messaging** product is free of cost to use. If other Firebase products are used, the cost will be charged as per the product. ## FAQs diff --git a/channels-and-providers/push/onesignal.mdx b/integrations/providers/push/onesignal.mdx similarity index 84% rename from channels-and-providers/push/onesignal.mdx rename to integrations/providers/push/onesignal.mdx index bab6199a..ab9da6ec 100644 --- a/channels-and-providers/push/onesignal.mdx +++ b/integrations/providers/push/onesignal.mdx @@ -1,6 +1,6 @@ --- -title: 'Onesignal' -description: 'Learn how to use the Onesignal provider to send push notifications using Novu' +title: "Onesignal" +description: "Learn how to use the Onesignal provider to send push notifications using Novu" --- [OneSignal](https://onesignal.com/) is a paid push notification service that supports sending messages via both [Apple Push Notification Service](https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server) (APNs) as well as [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging) (FCM). @@ -28,13 +28,14 @@ import { const novu = new Novu(""); await novu.subscribers.setCredentials('subscriberId', PushProviderIdEnum.OneSignal, { - // Your user's unique 'player_id' from OneSignal - deviceTokens: ['ad0452ca-3ca7-43b5-bf9b-fa93fd322035'], +// Your user's unique 'player_id' from OneSignal +deviceTokens: ['ad0452ca-3ca7-43b5-bf9b-fa93fd322035'], }); -``` + +```` -```bash +```bash curl -L -X PUT 'https://api.novu.co/v1/subscribers//credentials' \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ @@ -44,7 +45,8 @@ curl -L -X PUT 'https://api.novu.co/v1/subscribers//credentials' "deviceTokens": ["ad0452ca-3ca7-43b5-bf9b-fa93fd322035"], "integrationIdentifier": "one-signal-MnGLxp8uy" }' -``` +```` + @@ -53,18 +55,18 @@ Checkout the [API reference](/api-reference/subscribers/update-subscriber-creden # SDK Trigger Example ```jsx -import { Novu } from '@novu/node'; +import { Novu } from "@novu/node"; const novu = new Novu(""); -novu.trigger('', { +novu.trigger("", { to: { - subscriberId: '', + subscriberId: "", }, payload: { - abc: 'def', // If the notification is a data notification, the payload will be sent as the data + abc: "def", // If the notification is a data notification, the payload will be sent as the data }, }); ``` -Device/notification identifiers can be set by using [setCredentials](https://docs.novu.co/channels/email/resend/#set-device-token) or by using the `deviceIdentifiers` field in overrides. \ No newline at end of file +Device/notification identifiers can be set by using [setCredentials](https://docs.novu.co/channels/email/resend/#set-device-token) or by using the `deviceIdentifiers` field in overrides. diff --git a/channels-and-providers/push/overview.mdx b/integrations/providers/push/overview.mdx similarity index 89% rename from channels-and-providers/push/overview.mdx rename to integrations/providers/push/overview.mdx index 1ace71a3..00066cd4 100644 --- a/channels-and-providers/push/overview.mdx +++ b/integrations/providers/push/overview.mdx @@ -35,7 +35,7 @@ To send a push notification to subscribers (users) using Novu: Novu supports multiple active providers for push channel. - + ## Managing push device tokens @@ -43,6 +43,7 @@ To send a push notification to subscribers (users) using Novu: To send push notifications to subscribers, you need to store device tokens or identifiers in subscriber profiles. These tokens are unique identifiers that help push notification providers deliver messages to the correct devices. Each provider has its own method for obtaining and storing device tokens. Novu offers to ways of keeping your device tokens in sync with subscriber profiles: + - Just-in-time: Pass device tokens in the payload when triggering a workflow. Novu will automatically update subscriber profiles with the new device tokens. - Manual: Update subscriber profiles with device tokens using the Novu Set Credentials API. @@ -51,14 +52,14 @@ Novu offers to ways of keeping your device tokens in sync with subscriber profil When triggering a workflow, you can pass the `channels` array on the subscriber object with the device tokens for the push provider of your choice. Here is an example with fcm: ```typescript -novu.trigger('workflow-id', { +novu.trigger("workflow-id", { to: { - subscriberId: 'subscriber-id', + subscriberId: "subscriber-id", channels: [ { - providerId: 'fcm', + providerId: "fcm", credentials: { - deviceTokens: ['token-1', 'token-2'], + deviceTokens: ["token-1", "token-2"], }, }, ], @@ -89,29 +90,31 @@ const subscriber = await novu.subscribers.get('subscriberId'); // get current device tokens from subscriber credentials for the provider const currentDeviceTokens = subsciber.data.data.channels.find( - // _integrationId can also be checked in place of providerId ; - (channel) => channel.providerId === PushProviderIdEnum.FCM, +// \_integrationId can also be checked in place of providerId ; +(channel) => channel.providerId === PushProviderIdEnum.FCM, ).credentials.deviceTokens; // remove all device tokens await this.novu.subscribers.setCredentials( - 'subscriberId', - PushProviderIdEnum.FCM, - { deviceTokens: [] }, +'subscriberId', +PushProviderIdEnum.FCM, +{ deviceTokens: [] }, ); // remove the token you want to remove const newDeviceTokens = currentDeviceTokens.filter( - (token) => token !== 'token-to-be-removed', +(token) => token !== 'token-to-be-removed', ); // update subscriber credentials with new device tokens await this.novu.subscribers.setCredentials( - 'subscriberId', - PushProviderIdEnum.FCM, - { deviceTokens: newDeviceTokens }, +'subscriberId', +PushProviderIdEnum.FCM, +{ deviceTokens: newDeviceTokens }, ); + ``` +``` diff --git a/channels-and-providers/push/push-webhook.mdx b/integrations/providers/push/push-webhook.mdx similarity index 89% rename from channels-and-providers/push/push-webhook.mdx rename to integrations/providers/push/push-webhook.mdx index 2851235b..0805615b 100644 --- a/channels-and-providers/push/push-webhook.mdx +++ b/integrations/providers/push/push-webhook.mdx @@ -8,7 +8,7 @@ Users can use their own api url as webhook url and novu will make a post request ## Steps To Configure -1. Go to [integration store](https://web.novu.co/integrations?utm_campaign=docs-push-webhook) and click on `Add a provider` button. Choose `Push` channel and then `Push Webhook` provider. +1. Go to [integration store](https://dashboard.novu.co/integrations?utm_campaign=docs-push-webhook) and click on `Add a provider` button. Choose `Push` channel and then `Push Webhook` provider. 2. Enter your Webhook URL. For quick testing use [this](https://webhook.site/) website. 3. Enter Secret Hmac Key. Novu will use this secret hmac key to encrypt the data using `HMAC SHA256` algorithm and send the hash as value of `x-novu-signature` header. User can use `x-novu-signature` header to test authenticity of the request. Read more [here](#checking-authenticity) 4. Click on the update button. @@ -32,12 +32,13 @@ const novu = new Novu(""); // PushProviderIdEnum.PushWebhook = push-webhook await novu.subscribers.setCredentials('subscriberId', PushProviderIdEnum.PushWebhook, { - deviceTokens: ['ANY_RANDOM_STRING'], +deviceTokens: ['ANY_RANDOM_STRING'], }); -``` + +```` -```bash +```bash curl -L -X PUT 'https://api.novu.co/v1/subscribers//credentials' \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ @@ -47,24 +48,23 @@ curl -L -X PUT 'https://api.novu.co/v1/subscribers//credentials' "deviceTokens": ['ANY_RANDOM_STRING'], "integrationIdentifier": "push-webhook-MnGLxp8uy" }' -``` +```` + Checkout the [API reference](/api-reference/subscribers/update-subscriber-credentials) for more details. -## Example paylod sent by novu to webhook url +## Example paylod sent by novu to webhook url ```json { - "target": [ - "subscriber-token-for-push-webhook-provider" - ], + "target": ["subscriber-token-for-push-webhook-provider"], "title": "Push Webhook message title", "content": "push Webhook content body", "overrides": { "data": { - "custom_message": "this is custom message from payload push webhook demo", + "custom_message": "this is custom message from payload push webhook demo" } }, "payload": { @@ -83,9 +83,7 @@ Checkout the [API reference](/api-reference/subscribers/update-subscriber-creden "channels": [ { "credentials": { - "deviceTokens": [ - "subscriber-token-for-push-webhook-provider" - ] + "deviceTokens": ["subscriber-token-for-push-webhook-provider"] }, "_integrationId": "integrationId", "providerId": "push-webhook" diff --git a/channels-and-providers/push/pusher-beams.mdx b/integrations/providers/push/pusher-beams.mdx similarity index 84% rename from channels-and-providers/push/pusher-beams.mdx rename to integrations/providers/push/pusher-beams.mdx index 257ff653..a8e08541 100644 --- a/channels-and-providers/push/pusher-beams.mdx +++ b/integrations/providers/push/pusher-beams.mdx @@ -1,6 +1,6 @@ --- -title: 'Pusher Beams' -description: 'Learn how to use the Pusher Beams provider to send push notifications using Novu' +title: "Pusher Beams" +description: "Learn how to use the Pusher Beams provider to send push notifications using Novu" --- [Pusher Beams](https://pusher.com/beams/) is a cross-platform push notification API service provided by Pusher. @@ -28,13 +28,14 @@ import { const novu = new Novu(""); await novu.subscribers.setCredentials('subscriberId', PushProviderIdEnum.PusherBeams, { - // Your user's unique 'userId' from Pusher Beams - deviceTokens: ['userId-from-pusher-beams'], +// Your user's unique 'userId' from Pusher Beams +deviceTokens: ['userId-from-pusher-beams'], }); -``` + +```` -```bash +```bash curl -L -X PUT 'https://api.novu.co/v1/subscribers//credentials' \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ @@ -44,7 +45,8 @@ curl -L -X PUT 'https://api.novu.co/v1/subscribers//credentials' "deviceTokens": ['userId-from-pusher-beams'], "integrationIdentifier": "pusher-beams-MnGLxp8uy" }' -``` +```` + @@ -53,16 +55,16 @@ Checkout the [API reference](/api-reference/subscribers/update-subscriber-creden # SDK Trigger Example ```jsx -import { Novu } from '@novu/node'; +import { Novu } from "@novu/node"; const novu = new Novu(""); -novu.trigger('', { +novu.trigger("", { to: { - subscriberId: '', + subscriberId: "", }, payload: { - custom_data: 'custom_data', // the payload will be sent as notification data object. Cannot contain the key “pusher” + custom_data: "custom_data", // the payload will be sent as notification data object. Cannot contain the key “pusher” }, }); -``` \ No newline at end of file +``` diff --git a/channels-and-providers/push/pushpad.mdx b/integrations/providers/push/pushpad.mdx similarity index 87% rename from channels-and-providers/push/pushpad.mdx rename to integrations/providers/push/pushpad.mdx index c0771cd7..96ed2932 100644 --- a/channels-and-providers/push/pushpad.mdx +++ b/integrations/providers/push/pushpad.mdx @@ -1,6 +1,6 @@ --- -title: 'Pushpad' -description: 'Learn how to use the Pushpad provider to send web push notifications using Novu' +title: "Pushpad" +description: "Learn how to use the Pushpad provider to send web push notifications using Novu" --- [Pushpad](https://pushpad.xyz) is a web push service that supports sending notifications to all major browsers (Chrome, Firefox, Edge, Safari, etc.) via FCM, Mozilla autopush, Windows Push Notification Services and Apple Push Notification service, with just one simple API. @@ -28,13 +28,14 @@ import { const novu = new Novu(""); await novu.subscribers.setCredentials('subscriberId', PushProviderIdEnum.Pushpad, { - // the user ID (uid) that you used for Pushpad - deviceTokens: ['user123'], +// the user ID (uid) that you used for Pushpad +deviceTokens: ['user123'], }); -``` + +```` -```bash +```bash curl -L -X PUT 'https://api.novu.co/v1/subscribers//credentials' \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ @@ -44,7 +45,8 @@ curl -L -X PUT 'https://api.novu.co/v1/subscribers//credentials' "deviceTokens": ['user123'], "integrationIdentifier": "pushpad-MnGLxp8uy" }' -``` +```` + @@ -53,15 +55,15 @@ Checkout the [API reference](/api-reference/subscribers/update-subscriber-creden # SDK Trigger Example ```jsx -import { Novu } from '@novu/node'; +import { Novu } from "@novu/node"; const novu = new Novu(""); -novu.trigger('', { +novu.trigger("", { to: { - subscriberId: '', + subscriberId: "", }, - payload: {} + payload: {}, }); ``` diff --git a/channels-and-providers/sms/46elks.mdx b/integrations/providers/sms/46elks.mdx similarity index 71% rename from channels-and-providers/sms/46elks.mdx rename to integrations/providers/sms/46elks.mdx index 0dc0655a..a0b458a5 100644 --- a/channels-and-providers/sms/46elks.mdx +++ b/integrations/providers/sms/46elks.mdx @@ -1,6 +1,6 @@ --- -title: '46elks' -description: 'Learn how to use the 46elks provider to send sms notifications using Novu' +title: "46elks" +description: "Learn how to use the 46elks provider to send sms notifications using Novu" --- You can use the [46elks](https://46elks.com/) provider to send SMS messages to your customers using the Novu platform with a single API to create multi-channel experiences. @@ -11,11 +11,11 @@ To use the 46elks provider in the sms channel, you will need to create a 46elks # Creating the 46elks integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-46elks) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-46elks) page on Novu. - Click the "Add a provider" button. - Select 46elks service - Click `Next` - Choose your preferred deployment environment: `Development` or `Production`. Then Click the `Create` button. - Click on the `Disabled` button and mark it as `Active`. - Click on the **Update** button. -- You should now be able to send SMS, MMS, using 46elks in Novu. \ No newline at end of file +- You should now be able to send SMS, MMS, using 46elks in Novu. diff --git a/channels-and-providers/sms/africas-talking.mdx b/integrations/providers/sms/africas-talking.mdx similarity index 57% rename from channels-and-providers/sms/africas-talking.mdx rename to integrations/providers/sms/africas-talking.mdx index 6dfe7365..d51683f2 100644 --- a/channels-and-providers/sms/africas-talking.mdx +++ b/integrations/providers/sms/africas-talking.mdx @@ -5,7 +5,6 @@ description: "Learn how to use the Africa's Talking provider to send sms notific You can use [Africa's Talking](https://africastalking.com/) provider to send SMS messages to your customers using the Novu platform with a single API to create multi-channel experiences. - ## Getting Started To use Africa's Talking provider in the SMS channel, the first step is to create an Africa's Talking account and add your `API Key`, `username` and `Sender's ID` to Africa's Talking integration on the Novu platform. @@ -16,30 +15,41 @@ You’ll need to create an application to create a username in Africa’s Talkin - [Sign up](https://account.africastalking.com/auth/register) or [Log in](https://account.africastalking.com/auth/login) to your Africa's Talking account. - Select the team you want your app to be in. - + + {" "} + - If you're a new user and don't have a team yet, you'll have to do so by clicking on New Team and entering your team name. Click on Save when you're done. - + + {" "} + - On the page that appears, click on the Create App button. - On the pop-up that appears, enter your application name, username and select a country. Then click Save. The `username` is what you will use on the Novu platform. - + + {" "} + ## Generating an API key To generate a new API key, you can follow these steps: - -Ensure you have created an app in your team. - -- Click on the app you created. - -- On the page that appears, click on Settings(on the menu on your left). This will display a dropdown. Click on API Key from the dropdown options. - +Ensure you have created an app in your team.- Click on the app you created. + + {" "} + +- On the page that appears, click on Settings(on the menu on your left). This will +display a dropdown. Click on API Key from the dropdown options. + + {" "} + - On the page that appears, enter your password and click Generate. - -- Copy the API Key generated and paste it into the Novu platform or record it somewhere safe for later use because you will not see it from the dashboard on subsequent visits. + + {" "} + +- Copy the API Key generated and paste it into the Novu platform or record it somewhere +safe for later use because you will not see it from the dashboard on subsequent visits. -Once you've generated your API Key, wait about 3 minutes before testing it. + Once you've generated your API Key, wait about 3 minutes before testing it. ## Getting your Sender's ID @@ -49,25 +59,33 @@ Sender IDs allow you to brand your messages as you send them to your customers. To create a Short Code: - On your app dashboard, click on SMS (on the menu on your left). This will display a dropdown. Click on `Shortcodes` from the dropdown options. This will also display a dropdown from which you can then click on `My Shortcodes` to view your codes. - + + {" "} + - If you have not created one yet, on the `Shortcodes` dropdown option, click on `Request`. -On the page that appears, fill in the form and submit. - - + On the page that appears, fill in the form and submit. + + {" "} + ## To create an Alphanumeric: - On your app dashboard, click on SMS (on the menu on your left). This will display a dropdown. Click on `Alphanumerics` from the dropdown options. This will also display a dropdown from which you can then click on `My Alphanumerics` to view your codes. - + + + {" "} + - If you have not created one yet, on the `Alphanumerics` dropdown option, click on `Request`. -On the page that appears, fill in the form and submit - + On the page that appears, fill in the form and submit + + {" "} + - Once you're done, add either your short code or alphanumeric to the `from` field on the Novu platform. ## Creating an Africa's Talking integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-africastalking) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-africastalking) page on Novu. - Click the "Add a provider" button. - Locate **Africa's Talking** under the SMS section and click on the **Connect** button. - Enter the `API key`. @@ -76,4 +94,4 @@ On the page that appears, fill in the form and submit - Click on the `Disabled` button and mark it as `Active`. - Click on the **Connect** button. -Now it is possible to send SMS notifications using Africa's Talking in Novu. \ No newline at end of file +Now it is possible to send SMS notifications using Africa's Talking in Novu. diff --git a/channels-and-providers/sms/aws-sns.mdx b/integrations/providers/sms/aws-sns.mdx similarity index 82% rename from channels-and-providers/sms/aws-sns.mdx rename to integrations/providers/sms/aws-sns.mdx index 68f57d1e..fa4e6aaf 100644 --- a/channels-and-providers/sms/aws-sns.mdx +++ b/integrations/providers/sms/aws-sns.mdx @@ -1,6 +1,6 @@ --- -title: 'AWS SNS' -description: 'Learn how to use the AWS SNS provider to send sms notifications using Novu' +title: "AWS SNS" +description: "Learn how to use the AWS SNS provider to send sms notifications using Novu" --- You can use the [AWS SNS](https://aws.amazon.com/sns/) provider to send transactional emails to your customers using the Novu Platform with a single API to create multi-channel experiences. @@ -21,10 +21,10 @@ For security reasons, it is suggested that you create a new User to use with Nov # Create an AWS SNS integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-sms-sns) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-sms-sns) page on Novu. - Click the "Add a provider" button. - Locate **Amazon SNS** and click on the **Connect** button. - Enter your `Access Key ID`, `Secret Access key`, and `AWS region`. - Click on the `Disabled` button and mark it as `Active`. - Click on the **Save** button. -- You should now be able to send SMS notifications using **Amazon SNS** in Novu. \ No newline at end of file +- You should now be able to send SMS notifications using **Amazon SNS** in Novu. diff --git a/channels-and-providers/sms/azure.mdx b/integrations/providers/sms/azure.mdx similarity index 80% rename from channels-and-providers/sms/azure.mdx rename to integrations/providers/sms/azure.mdx index 0611850e..360dbf42 100644 --- a/channels-and-providers/sms/azure.mdx +++ b/integrations/providers/sms/azure.mdx @@ -1,6 +1,6 @@ --- -title: 'Azure SMS' -description: 'Learn how to use the Azure SMS provider to send sms notifications using Novu' +title: "Azure SMS" +description: "Learn how to use the Azure SMS provider to send sms notifications using Novu" --- You can use the [Azure SMS](https://learn.microsoft.com/en-us/azure/communication-services/quickstarts/sms/send?tabs=windows&pivots=platform-azcli) SMS provider to send SMS messages to the customers using the Novu Platform with a single API to create multi-channel experiences. @@ -11,7 +11,7 @@ To use Azure SMS provider in the SMS channel, the first step is to create an Azu # Creating a Azure SMS integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-sms-azure) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-sms-azure) page on Novu. - Click the "Add a provider" button. - Locate **Azure Sms** under the SMS section and click on the **Next** button. - Select Your Environment add condition (Optional). @@ -21,4 +21,4 @@ To use Azure SMS provider in the SMS channel, the first step is to create an Azu - Enter the `From` value. - Click on the **Update** button. -Now it is possible to send SMS notifications using **Azure Sms** in Novu. \ No newline at end of file +Now it is possible to send SMS notifications using **Azure Sms** in Novu. diff --git a/channels-and-providers/sms/bulk-sms.mdx b/integrations/providers/sms/bulk-sms.mdx similarity index 78% rename from channels-and-providers/sms/bulk-sms.mdx rename to integrations/providers/sms/bulk-sms.mdx index 191160c1..61d8722f 100644 --- a/channels-and-providers/sms/bulk-sms.mdx +++ b/integrations/providers/sms/bulk-sms.mdx @@ -1,6 +1,6 @@ --- -title: 'BulkSMS' -description: 'Learn how to use the BulkSMS provider to send sms notifications using Novu' +title: "BulkSMS" +description: "Learn how to use the BulkSMS provider to send sms notifications using Novu" --- You can use the [BurstSMS](https://bulksms.com/) provider to send SMS messages to the customers using the Novu Platform with a single API to create multi-channel experiences. @@ -11,7 +11,7 @@ To use BulkSMS provider in the SMS channel, the first step is to create a BulkSM # Creating a BulkSMS integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-sms-bulksms) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-sms-bulksms) page on Novu. - Click the "Add a provider" button. - Locate **BulkSMS** under the SMS section and click on the **Next** button. - Select Your Environment add condition (Optional). @@ -20,4 +20,4 @@ To use BulkSMS provider in the SMS channel, the first step is to create a BulkSM - Enter the `API Token`. - Click on the **Update** button. -Now it is possible to send SMS notifications using **BulkSMS** in Novu. \ No newline at end of file +Now it is possible to send SMS notifications using **BulkSMS** in Novu. diff --git a/channels-and-providers/sms/burst-sms.mdx b/integrations/providers/sms/burst-sms.mdx similarity index 82% rename from channels-and-providers/sms/burst-sms.mdx rename to integrations/providers/sms/burst-sms.mdx index 7a20793d..00472210 100644 --- a/channels-and-providers/sms/burst-sms.mdx +++ b/integrations/providers/sms/burst-sms.mdx @@ -1,6 +1,6 @@ --- -title: 'BurstSMS' -description: 'Learn how to use the BurstSMS provider to send sms notifications using Novu' +title: "BurstSMS" +description: "Learn how to use the BurstSMS provider to send sms notifications using Novu" --- You can use the [BurstSMS](https://burstsms.com/) provider to send SMS messages to the customers using the Novu Platform with a single API to create multi-channel experiences. @@ -15,11 +15,11 @@ To find the BurstSMS API and Secret key, log into the personal BurstSMS account # Creating a BurstSMS integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-sms-burstsms) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-sms-burstsms) page on Novu. - Click the "Add a provider" button. - Locate **BurstSMS** under the SMS section and click on the **Next** button. - Select Your Environment add condition (Optional) - Click on the `Disabled` button and mark it as `Active`. - Enter the `API key` and `API Secret`. - Click on the **Upload** button. -- Now it is possible to send SMS notifications using **BurstSMS** in Novu. \ No newline at end of file +- Now it is possible to send SMS notifications using **BurstSMS** in Novu. diff --git a/channels-and-providers/sms/clickatell.mdx b/integrations/providers/sms/clickatell.mdx similarity index 51% rename from channels-and-providers/sms/clickatell.mdx rename to integrations/providers/sms/clickatell.mdx index df9ceeca..74d8b62b 100644 --- a/channels-and-providers/sms/clickatell.mdx +++ b/integrations/providers/sms/clickatell.mdx @@ -1,6 +1,6 @@ --- -title: 'Clickatell' -description: 'Learn how to use the Clickatell provider to send sms notifications using Novu' +title: "Clickatell" +description: "Learn how to use the Clickatell provider to send sms notifications using Novu" --- You can use the [Clickatell](https://www.clickatell.com/) provider to send SMS messages to your customers using the Novu Platform. @@ -8,7 +8,7 @@ Let's see how to do it: ## Getting Started -To use the [Clickatell](https://www.clickatell.com/) provider in the SMS channel, the first step is to [create a Clickatell account](https://www.clickatell.com/sign-up/) and add your API key to the Clickatell integration on the [Novu web dashboard](https://web.novu.co/integrations?utm_campaign=docs-sms-clickatell). +To use the [Clickatell](https://www.clickatell.com/) provider in the SMS channel, the first step is to [create a Clickatell account](https://www.clickatell.com/sign-up/) and add your API key to the Clickatell integration on the [Novu web dashboard](https://dashboard.novu.co/integrations?utm_campaign=docs-sms-clickatell). ## Retrieving your API Key @@ -16,26 +16,43 @@ To find your Clickatell API key: - [Sign up](https://www.clickatell.com/sign-up/) or [Login](https://app.clickatell.com/signin) to your Clickatell account. - Click on the `My Workspace`, and then click `SMS` in Channels Section. - + + + {" "} + - Click on the `New SMS Setup`. - + + + {" "} + - Select `API` then `Messaging Type` and click Next. - + + + {" "} + - Select `Basic HTTP API` then `create a new HTTP API` and click Next. - + + + {" "} + - Now fill in all details and Click Next. - + + + {" "} + - Copy the API key and click complete. - + + {" "} + ## Create a Clickatell integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-sms-clickatell) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-sms-clickatell) page on Novu. - Click the "Add a provider" button. - Locate **Clickatell** under the SMS section and click on the **Connect** button. - Enter your Clickatell API Key. diff --git a/channels-and-providers/sms/clicksend.mdx b/integrations/providers/sms/clicksend.mdx similarity index 83% rename from channels-and-providers/sms/clicksend.mdx rename to integrations/providers/sms/clicksend.mdx index 2a93bcff..75553157 100644 --- a/channels-and-providers/sms/clicksend.mdx +++ b/integrations/providers/sms/clicksend.mdx @@ -1,6 +1,6 @@ --- -title: 'Clicksend' -description: 'Learn how to use the Clicsend provider to send sms notifications using Novu' +title: "Clicksend" +description: "Learn how to use the Clicsend provider to send sms notifications using Novu" --- You can use the [Clicksend](https://www.clicksend.com/in/) provider to send SMS messages to your customers using the Novu Platform. @@ -8,7 +8,7 @@ Let's see how to do it: # Getting Started -To use the [Clicksend](https://www.clicksend.com/in/) provider in the SMS channel, the first step is to [create a Clicksend account](https://dashboard.clicksend.com/signup/step1) and add your API key to the Clicksend integration on the [Novu web dashboard](https://web.novu.co/integrations?utm_campaign=docs-sms-clicksend). +To use the [Clicksend](https://www.clicksend.com/in/) provider in the SMS channel, the first step is to [create a Clicksend account](https://dashboard.clicksend.com/signup/step1) and add your API key to the Clicksend integration on the [Novu web dashboard](https://dashboard.novu.co/integrations?utm_campaign=docs-sms-clicksend). # Retrieving your API Key @@ -18,7 +18,7 @@ To use the [Clicksend](https://www.clicksend.com/in/) provider in the SMS channe # Create a Clicksend integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-sms-clicksend) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-sms-clicksend) page on Novu. - Click on Add a Provider. - Select Clicksend service. - Choose your preferred deployment environment: `Development` or `Production`. Then Click the `Create` button. diff --git a/channels-and-providers/sms/firetext.mdx b/integrations/providers/sms/firetext.mdx similarity index 63% rename from channels-and-providers/sms/firetext.mdx rename to integrations/providers/sms/firetext.mdx index bfaa5eb1..9b151ec3 100644 --- a/channels-and-providers/sms/firetext.mdx +++ b/integrations/providers/sms/firetext.mdx @@ -1,6 +1,6 @@ --- -title: 'Firetext' -description: 'Learn how to use the firetext provider to send sms notifications using Novu' +title: "Firetext" +description: "Learn how to use the firetext provider to send sms notifications using Novu" --- You can use the [firetext](https://www.firetext.co.uk) provider to send SMS messages to your customers using the Novu Platform with a single API to create multi-channel experiences. @@ -8,8 +8,8 @@ You can use the [firetext](https://www.firetext.co.uk) provider to send SMS me ## Getting Started To use the firetext provider in the SMS channel, the first step is to create a firetext account and add your API key and Sender ID to the firetext integration on the Novu platform. -- Note : Firetext is only available in uk region. Others may require additional verification. +- Note : Firetext is only available in uk region. Others may require additional verification. ## Retrieving your API Key @@ -17,18 +17,25 @@ To find your firetext API key: - [Sign up](https://app.firetext.co.uk/signup) or [Login](https://app.firetext.co.uk/) to your firetext account. - Click on the `Settings` icon in the top right corner of the screen. - + + + {" "} + - Now Click on the `API` in `My Settings` Section. - -- On the API Keys page, copy the `API key`. - + + {" "} + +- On the API Keys page, copy the `API key`. + + {" "} + ## Create a firetext integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-sms-firetxt) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-sms-firetxt) page on Novu. - Click the "Add a provider" button. - Locate **firetext** under the SMS section and click on the **Connect** button. - Enter your `firetext` API Key. diff --git a/channels-and-providers/sms/gupshup.mdx b/integrations/providers/sms/gupshup.mdx similarity index 82% rename from channels-and-providers/sms/gupshup.mdx rename to integrations/providers/sms/gupshup.mdx index 77909c2f..5a6612ff 100644 --- a/channels-and-providers/sms/gupshup.mdx +++ b/integrations/providers/sms/gupshup.mdx @@ -1,6 +1,6 @@ --- -title: 'gupshup' -description: 'Learn how to use the gupshup provider to send sms notifications using Novu' +title: "gupshup" +description: "Learn how to use the gupshup provider to send sms notifications using Novu" --- You can use the [gupshup](https://www.gupshup.io/) provider to send SMS messages to your customers using the Novu Platform with a single API to create multi-channel experiences. @@ -16,7 +16,7 @@ To use the gupshup provider in the SMS channel, the first step is to create a gu # Creating a gupshup integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-sms-gupshup) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-sms-gupshup) page on Novu. - Click the "Add a provider" button. - Locate **gupshup** under the SMS section and click on the **Connect** button. - Enter the `User id`. diff --git a/channels-and-providers/sms/infobip.mdx b/integrations/providers/sms/infobip.mdx similarity index 67% rename from channels-and-providers/sms/infobip.mdx rename to integrations/providers/sms/infobip.mdx index 78fd8960..8ae2e03a 100644 --- a/channels-and-providers/sms/infobip.mdx +++ b/integrations/providers/sms/infobip.mdx @@ -1,6 +1,6 @@ --- -title: 'Infobip - SMS' -description: 'Learn how to use the Infobip provider to send sms notifications using Novu' +title: "Infobip - SMS" +description: "Learn how to use the Infobip provider to send sms notifications using Novu" --- You can use the [Infobip](https://www.infobip.com/developers/) provider to send SMS messages to your customers using the Novu Platform with a single API to create multi-channel experiences. @@ -16,30 +16,38 @@ To Find a new API key in Infobip, you can follow these steps: - [Sign up](https://www.infobip.com/signup) or [Log in](https://portal.infobip.com/login/) to your Infobip account. - On the [Homepage](https://portal.infobip.com/homepage/), you'll see your API key. - + + {" "} + Alternatively, you can find the API key on the Developer Tools page. - On the sidebar (on the left), click on `Developer Tools`. - This will display a dropdown from which you can then click on `API Ksys` to view your API key. - + + {" "} + If you want to create an API key: - Click on the `CREATE API KEY` button - Fill the form - Click on `GENERATE` - + + {" "} + ## Finding the Base URL To see your base URL, log in to the account. Once logged in, on the Homepage, you should see your base URL in this format: `xxxxx.api.infobip.com`. - + + {" "} + ## Create an Infobip integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-sms-infobip) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-sms-infobip) page on Novu. - Click the "Add a provider" button. - Locate **Infobip** under the SMS section and click on the **Connect** button. - Enter your Infobip API Key. @@ -48,4 +56,4 @@ To see your base URL, log in to the account. Once logged in, on the Homepage, yo - Click on the `Disabled` button and mark it as `Active`. - Click on the **Connect** button. -You should now be able to send SMS notifications using Infobip in Novu. \ No newline at end of file +You should now be able to send SMS notifications using Infobip in Novu. diff --git a/channels-and-providers/sms/kannel.mdx b/integrations/providers/sms/kannel.mdx similarity index 75% rename from channels-and-providers/sms/kannel.mdx rename to integrations/providers/sms/kannel.mdx index 1b91c624..1f907863 100644 --- a/channels-and-providers/sms/kannel.mdx +++ b/integrations/providers/sms/kannel.mdx @@ -1,16 +1,16 @@ --- -title: 'Kannel' -description: 'Learn how to use the Kannel sms provider to send sms notifications using Novu' +title: "Kannel" +description: "Learn how to use the Kannel sms provider to send sms notifications using Novu" --- Before integrating Kannel with Novu, you should have Kannel set up and configured as an SMS gateway on your server. Let’s look at how you can do that: # Setting up Kannel -1. First, you need to install Kannel on your server. You can download it from the [official-website](https://www.kannel.org/download.shtml) or use a package manager specific to your operating system (e.g., apt- +1. First, you need to install Kannel on your server. You can download it from the [official-website](https://www.kannel.org/download.shtml) or use a package manager specific to your operating system (e.g., apt- get for Ubuntu, yum for CentOS, etc.). 2. Kannel's configuration is done through a file called kannel.conf. You need to edit this file to specify your SMS provider settings. Here's a basic configuration example: - + ```bash group = smsc smsc = smpp @@ -23,31 +23,34 @@ Before integrating Kannel with Novu, you should have Kannel set up and configure max-pending-submits = 10 allow-ip = "127.0.0.1" ``` + Replace the placeholders (e.g., **YourSMSCID**, **SMSC_Hostname_or_IP**, **SMSC_Port**, **SMSC_System_Type**, **YourUsername**, and **YourPassword**) with the actual values provided by your SMS provider. 3. You can define services that will handle incoming and outgoing SMS messages. These services specify how Kannel should process SMS requests. Here's an example of an SMS service configuration: - - ```bash + + ```bash group = sendsms-user username = YourUsername password = YourPassword concatenation = true max-messages = 3 ``` + Adjust the settings to your needs. 4. Start Kannel with the following command: - - ```bash - bearerbox /path/to/kannel.conf - ``` - Make sure to replace /path/to/kannel.conf with the actual path to your Kannel configuration file. + + ```bash + bearerbox /path/to/kannel.conf + ``` + + Make sure to replace /path/to/kannel.conf with the actual path to your Kannel configuration file. # Creating a Kannel integration with Novu - After setting up your Kannel, follow these steps to integrate with novu: +After setting up your Kannel, follow these steps to integrate with novu: -- Visit the [Integrations Store](https://web.novu.co/integrations?utm_campaign=docs-sms-kannel) on Novu. +- Visit the [Integrations Store](https://dashboard.novu.co/integrations?utm_campaign=docs-sms-kannel) on Novu. - Click the "Add a provider" button. - Select Kannel service. - Choose your preferred deployment environment: `Development` or `Production`. Then Click the `Create` button. @@ -57,24 +60,20 @@ Before integrating Kannel with Novu, you should have Kannel set up and configure - **Port**: The port number through which Novu should communicate with Kannel (usually 13013, or a custom port you've configured in Kannel). - **Username and Password**: If you've set up authentication for your Kannel SMS gateway, provide the username and password required for authentication: - ```bash - group = smsc - smsc = smpp - smsc-id = YourSMSCID - host = SMSC_Hostname_or_IP - port = SMSC_Port - smsc-username = YourUsername - smsc-password = YourPassword - max-pending-submits = 10 - allow-ip = "127.0.0.1" - ``` - In this example, **YourUsername** and **YourPassword** are the credentials you'd use for authentication. + ```bash + group = smsc + smsc = smpp + smsc-id = YourSMSCID + host = SMSC_Hostname_or_IP + port = SMSC_Port + smsc-username = YourUsername + smsc-password = YourPassword + max-pending-submits = 10 + allow-ip = "127.0.0.1" + ``` + + In this example, **YourUsername** and **YourPassword** are the credentials you'd use for authentication. - Fill in the `From` field. - Click on the `Update` button. - You should now be able to send notifications using Kannel in Novu. - - - - - diff --git a/channels-and-providers/sms/messagebird.mdx b/integrations/providers/sms/messagebird.mdx similarity index 80% rename from channels-and-providers/sms/messagebird.mdx rename to integrations/providers/sms/messagebird.mdx index 4008d21d..72b506b8 100644 --- a/channels-and-providers/sms/messagebird.mdx +++ b/integrations/providers/sms/messagebird.mdx @@ -1,6 +1,6 @@ --- -title: 'MessageBird' -description: 'Learn how to use the messagebird provider to send sms notifications using Novu' +title: "MessageBird" +description: "Learn how to use the messagebird provider to send sms notifications using Novu" --- You can use the [MessageBird](https://www.messagebird.com) provider to send SMS messages to your customers using the Novu Platform with a single API to create multi-channel experiences. @@ -11,7 +11,7 @@ To use the MessageBird provider in the SMS channel, the first step is to create # Creating a MessageBird integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-sms-messagebird) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-sms-messagebird) page on Novu. - Click the "Add a provider" button. - Locate **MessageBird** under the SMS section. Click it and the **Next** button. - Select Your Environment add condition (Optional). @@ -20,4 +20,4 @@ To use the MessageBird provider in the SMS channel, the first step is to create - Enter the `From` value. - Click on the **Update** button. -Now it is possible to send SMS notifications using **MessageBird** in Novu. \ No newline at end of file +Now it is possible to send SMS notifications using **MessageBird** in Novu. diff --git a/channels-and-providers/sms/nexmo.mdx b/integrations/providers/sms/nexmo.mdx similarity index 82% rename from channels-and-providers/sms/nexmo.mdx rename to integrations/providers/sms/nexmo.mdx index 9d0a55ef..850e1519 100644 --- a/channels-and-providers/sms/nexmo.mdx +++ b/integrations/providers/sms/nexmo.mdx @@ -1,6 +1,6 @@ --- -title: 'Nexmo' -description: 'Learn how to use the Nexmo provider to send sms notifications using Novu' +title: "Nexmo" +description: "Learn how to use the Nexmo provider to send sms notifications using Novu" --- You can use the [Nexmo By Vonage](https://www.vonage.com/) provider to send SMS messages to your customers using the Novu Platform with a single API to create multi-channel experiences. @@ -15,11 +15,11 @@ First, [sign up for a Vonage account](https://ui.idp.vonage.com/ui/auth/login) # Create a Nexmo integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-sms-nexmo) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-sms-nexmo) page on Novu. - Click the "Add a provider" button. - Locate **Nexmo** under the SMS section and click on the **Connect** button. - Enter the `API key` and `API Secret`. - Enter a valid `From` email address. - Click on the `Disabled` button and mark it as `Active`. - Click on the **Connect** button. -- Now it is possible to send SMS notifications using **Nexmo** in Novu. \ No newline at end of file +- Now it is possible to send SMS notifications using **Nexmo** in Novu. diff --git a/channels-and-providers/sms/overview.mdx b/integrations/providers/sms/overview.mdx similarity index 90% rename from channels-and-providers/sms/overview.mdx rename to integrations/providers/sms/overview.mdx index 9c3e535f..ff9cedd2 100644 --- a/channels-and-providers/sms/overview.mdx +++ b/integrations/providers/sms/overview.mdx @@ -3,11 +3,18 @@ title: "SMS Channel Overview" sidebarTitle: "Overview" description: "Learn the process of configuring and using sms providers with Novu" --- + import { MissingProvider } from "/snippets/missing-provider.mdx"; Novu can be used to deliver sms messages to your customers using a unified delivery API. You can easily integrate your favorite sms provider using the built-in integration store. - + ## Configuring SMS providers @@ -66,15 +73,16 @@ import { Novu } from '@novu/node'; const novu = new Novu(''); novu.trigger('', { - to: { - subscriberId: '', - }, - overrides: { - sms: { - integrationIdentifier: "infobip-abcdef" - }, - }, +to: { +subscriberId: '', +}, +overrides: { +sms: { +integrationIdentifier: "infobip-abcdef" +}, +}, }); + ``` @@ -90,4 +98,5 @@ Common errors and reason for these errors while sending sms messages using Novu. - \ No newline at end of file + +``` diff --git a/channels-and-providers/sms/plivo.mdx b/integrations/providers/sms/plivo.mdx similarity index 83% rename from channels-and-providers/sms/plivo.mdx rename to integrations/providers/sms/plivo.mdx index 71ad083a..f7b5783d 100644 --- a/channels-and-providers/sms/plivo.mdx +++ b/integrations/providers/sms/plivo.mdx @@ -1,6 +1,6 @@ --- -title: 'Plivo' -description: 'Learn how to use the Plivo provider to send sms notifications using Novu' +title: "Plivo" +description: "Learn how to use the Plivo provider to send sms notifications using Novu" --- You can use the [Plivo](https://www.plivo.com/) provider to send SMS messages to your customers using the Novu Platform with a single API to create multi-channel experiences. @@ -15,7 +15,7 @@ To use the Plivo provider in the SMS channel, the first step is to create a Pliv # Creating a Plivo integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-sms-plivo) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-sms-plivo) page on Novu. - Click the "Add a provider" button. - Locate **Plivo** under the SMS section and click on the **Connect** button. - Enter the `Account SID`. @@ -24,4 +24,4 @@ To use the Plivo provider in the SMS channel, the first step is to create a Pliv - Click on the `Disabled` button and mark it as `Active`. - Click on the **Connect** button. -Now it is possible to send SMS notifications using **Plivo** in Novu. \ No newline at end of file +Now it is possible to send SMS notifications using **Plivo** in Novu. diff --git a/channels-and-providers/sms/sendchamp.mdx b/integrations/providers/sms/sendchamp.mdx similarity index 62% rename from channels-and-providers/sms/sendchamp.mdx rename to integrations/providers/sms/sendchamp.mdx index c1edd0c7..2fbab74c 100644 --- a/channels-and-providers/sms/sendchamp.mdx +++ b/integrations/providers/sms/sendchamp.mdx @@ -1,6 +1,6 @@ --- -title: 'Sendchamp' -description: 'Learn how to use the Sendchamp provider to send sms notifications using Novu' +title: "Sendchamp" +description: "Learn how to use the Sendchamp provider to send sms notifications using Novu" --- You can use the [Sendchamp](https://www.sendchamp.com/) provider to send SMS messages to your customers using the Novu Platform with a single API to create multi-channel experiences. @@ -15,14 +15,21 @@ To find your Sendchamp API key: - [Sign up](https://my.sendchamp.com/signup) or [Login](https://my.sendchamp.com/login) to your Sendchamp account. - Click on the Avatar icon in the top right corner of the screen, and then click `API & Integrations` from the drop-down menu. - -Alternatively, you can access the API key from the Accounts menu. + + + {" "} + + Alternatively, you can access the API key from the Accounts menu. - Scroll to the bottom of the sidebar (on the left) and click on `Accounts`. - This will display a dropdown from which you can then click on `API keys & Webhooks` to view your API key. - + + {" "} + - On the API Keys page, copy the Public access key. - + + {" "} + ## Get your Sender's ID @@ -32,19 +39,24 @@ To get your Sender's ID: - On the sidebar (on the menu on your left), click on `SMS`. This will display a dropdown. Click on `Sender ID` from the dropdown options. - On the page that appears, you'll find a list of your Sender IDs - -If you have not created one yet: + + + {" "} + + If you have not created one yet: - Click on the `Create Sender ID` button to request a Sender ID. - Fill in the form. - Click on `Add Sender ID` button. - + + {" "} + Once it is approved, you can use your Sender ID as the from field on the Novu platform. ## Create a Sendchamp integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-sms-sendchamp) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-sms-sendchamp) page on Novu. - Click the "Add a provider" button. - Locate **Sendchamp** under the SMS section and click on the **Connect** button. - Enter your Sendchamp API Key. @@ -52,4 +64,4 @@ Once it is approved, you can use your Sender ID as the from field on the Novu - Click on the `Disabled` button and mark it as `Active`. - Click on the **Connect** button. -Now it is possible to send SMS notifications using **Sendchamp** in Novu. \ No newline at end of file +Now it is possible to send SMS notifications using **Sendchamp** in Novu. diff --git a/channels-and-providers/sms/simpletexting.mdx b/integrations/providers/sms/simpletexting.mdx similarity index 76% rename from channels-and-providers/sms/simpletexting.mdx rename to integrations/providers/sms/simpletexting.mdx index 6875c9ba..49ba86f7 100644 --- a/channels-and-providers/sms/simpletexting.mdx +++ b/integrations/providers/sms/simpletexting.mdx @@ -1,6 +1,6 @@ --- -title: 'SimpleTexting' -description: 'Learn how to use the SimpleTexting provider to send sms notifications using Novu' +title: "SimpleTexting" +description: "Learn how to use the SimpleTexting provider to send sms notifications using Novu" --- You can use the [SimpleTexting](https://simpletexting.com/) SMS provider to send SMS messages to the customers using the Novu Platform with a single API to create multi-channel experiences. @@ -11,7 +11,7 @@ To use SimpleTexting provider in the SMS channel, the first step is to create a # Creating a SimpleTexting integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-sms-simpletexting) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-sms-simpletexting) page on Novu. - Click the "Add a provider" button. - Locate **SimpleTexting** under the SMS section and click on the **Next** button. - Select Your Environment add condition (Optional). @@ -21,4 +21,4 @@ To use SimpleTexting provider in the SMS channel, the first step is to create a - Enter the `From` value. - Click on the **Update** button. -Now it is possible to send SMS notifications using **SimpleTexting** in Novu. \ No newline at end of file +Now it is possible to send SMS notifications using **SimpleTexting** in Novu. diff --git a/channels-and-providers/sms/sms-central.mdx b/integrations/providers/sms/sms-central.mdx similarity index 78% rename from channels-and-providers/sms/sms-central.mdx rename to integrations/providers/sms/sms-central.mdx index 1a324e95..719be3d8 100644 --- a/channels-and-providers/sms/sms-central.mdx +++ b/integrations/providers/sms/sms-central.mdx @@ -1,25 +1,23 @@ --- -title: 'SMS Central' -description: 'Learn how to use the SMS Central provider to send sms notifications using Novu' +title: "SMS Central" +description: "Learn how to use the SMS Central provider to send sms notifications using Novu" --- You can use the [SMS Central](https://www.smscentral.com.au/) provider to send SMS messages to your customers using the Novu platform with a single API to create multi-channel experiences. - # Getting Started To use the SMS Central provider in the sms channel, you will need to create a SMS Central account and add your SMS Central `Username` & `Password` to the SMS Central integration on the Novu platform. - # Creating the SMS Central integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-sms-smscentral) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-sms-smscentral) page on Novu. - Click the "Add a provider" button. - Select SMS Central service - Click `Next` - Choose your preferred deployment environment: `Development` or `Production`. Then Click the `Create` button. - Add `Username` & `Password` -- Fill up the `From` section with the number you wish to send sms from. +- Fill up the `From` section with the number you wish to send sms from. - Click on the `Disabled` button and mark it as `Active`. - Click on the **Update** button. -- You should now be able to send notifications using SMS Central in Novu. \ No newline at end of file +- You should now be able to send notifications using SMS Central in Novu. diff --git a/channels-and-providers/sms/sms77.mdx b/integrations/providers/sms/sms77.mdx similarity index 81% rename from channels-and-providers/sms/sms77.mdx rename to integrations/providers/sms/sms77.mdx index 8aaaa3fe..386dba88 100644 --- a/channels-and-providers/sms/sms77.mdx +++ b/integrations/providers/sms/sms77.mdx @@ -1,6 +1,6 @@ --- -title: 'SMS77' -description: 'Learn how to use the SMS77 provider to send sms notifications using Novu' +title: "SMS77" +description: "Learn how to use the SMS77 provider to send sms notifications using Novu" --- It is possible to use the [SMS77](https://www.sms77.io/en) provider to send SMS messages to the customers using the Novu Platform with a single API to create multi-channel experiences. @@ -15,10 +15,10 @@ To find the SMS77 API key, log into the personal SMS77 account and navigate to t # Creating an SMS77 integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-sms-sms77) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-sms-sms77) page on Novu. - Click the "Add a provider" button. - Locate **SMS77** under the SMS section and click on the **Connect** button. - Enter the `API key`. - Click on the `Disabled` button and mark it as `Active`. - Click on the **Save** button. -- Now it is possible to send SMS notifications using **SMS77** in Novu. \ No newline at end of file +- Now it is possible to send SMS notifications using **SMS77** in Novu. diff --git a/channels-and-providers/sms/sns.mdx b/integrations/providers/sms/sns.mdx similarity index 88% rename from channels-and-providers/sms/sns.mdx rename to integrations/providers/sms/sns.mdx index cf1646a5..12f3ffb7 100644 --- a/channels-and-providers/sms/sns.mdx +++ b/integrations/providers/sms/sns.mdx @@ -1,6 +1,6 @@ --- -title: 'SNS' -description: 'Learn how to use the SNS provider to send sms notifications using Novu' +title: "SNS" +description: "Learn how to use the SNS provider to send sms notifications using Novu" --- You can use the [SNS](https://aws.amazon.com/sns/) provider to send transactional emails to your customers using the Novu Platform with a single API to create multi-channel experiences. @@ -16,10 +16,10 @@ Before you can use SNS as your SMS provider in the Novu platform, you'll need to # Create an SNS integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations?utm_campaign=docs-sms-sns) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations?utm_campaign=docs-sms-sns) page on Novu. - Click the "Add a provider" button. - Choose your preferred deployment environment: `Development` or `Production`. Then Click the `Create` button. - Enter your `Access Key ID`, `Secret Access key`, and `AWS region`. - Click on the `Disabled` button and mark it as `Active`. - Click on the `Update` button. -- You should now be able to send SMS notifications using **SNS** in Novu. \ No newline at end of file +- You should now be able to send SMS notifications using **SNS** in Novu. diff --git a/channels-and-providers/sms/telnyx.mdx b/integrations/providers/sms/telnyx.mdx similarity index 90% rename from channels-and-providers/sms/telnyx.mdx rename to integrations/providers/sms/telnyx.mdx index a562052d..d74ce65e 100644 --- a/channels-and-providers/sms/telnyx.mdx +++ b/integrations/providers/sms/telnyx.mdx @@ -1,6 +1,6 @@ --- -title: 'Telnyx' -description: 'Learn how to use the Telnyx provider to send sms notifications using Novu' +title: "Telnyx" +description: "Learn how to use the Telnyx provider to send sms notifications using Novu" --- You can use the [Telnyx](https://telnyx.com/) provider to send transactional emails to your customers using the Novu Platform with a single API to create multi-channel experiences. @@ -36,7 +36,7 @@ A valid from address must be a valid phone number in +E.164 format, a short code # Creating a Telnyx integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations) page on the Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations) page on the Novu. - Click the "Add a provider" button. - Locate Telnyx and click on the **Connect** button. - Enter your Telnyx API Key. @@ -44,4 +44,4 @@ A valid from address must be a valid phone number in +E.164 format, a short code - Enter the Valid From address. - Click on the `Disabled` button and `mark it as Active`. - Click on the **Save** button. -- You should now be able to send notifications using Telnyx in Novu. \ No newline at end of file +- You should now be able to send notifications using Telnyx in Novu. diff --git a/channels-and-providers/sms/termii.mdx b/integrations/providers/sms/termii.mdx similarity index 70% rename from channels-and-providers/sms/termii.mdx rename to integrations/providers/sms/termii.mdx index 96eb4953..c6523dae 100644 --- a/channels-and-providers/sms/termii.mdx +++ b/integrations/providers/sms/termii.mdx @@ -1,6 +1,6 @@ --- -title: 'Termii' -description: 'Learn how to use the Termii provider to send sms notifications using Novu' +title: "Termii" +description: "Learn how to use the Termii provider to send sms notifications using Novu" --- You can use the [Termii](https://termii.com/) provider to send SMS messages to your customers using the Novu Platform with a single API to create multi-channel experiences. @@ -16,12 +16,17 @@ To find your Termii API key: - [Sign up](https://accounts.termii.com/#/register) or [Login](https://accounts.termii.com/#/login) to your Termii account. - Navigate to your [dashboard](https://accounts.termii.com/#/). - Then scroll to the bottom of the page to find your API key. - -Alternatively, you can find the API key on the Settings page. + + + {" "} + + Alternatively, you can find the API key on the Settings page. - Scroll to the bottom of the sidebar (on the left) and click on settings. - This will display a dropdown from which you can then click on `API Token` to view your API key. - + + {" "} + ## Getting your Sender's ID @@ -32,7 +37,9 @@ To get your Sender's ID: - On the sidebar (on the menu on your left), click on `Rental`. This will display a dropdown. Click on `SMS Sender IDs` from the dropdown options. - On the page that appears, you'll find a list of your Sender IDs - + + {" "} + If you have not created one yet: @@ -40,11 +47,13 @@ If you have not created one yet: - Fill in the form - Click on `Save` - + + {" "} + ## Creating a Termii integration with Novu -- Visit the [Integrations](https://web.novu.co/integrations) page on Novu. +- Visit the [Integrations](https://dashboard.novu.co/integrations) page on Novu. - Click the "Add a provider" button. - Locate **Termii** under the SMS section and click on the **Connect** button. - Enter your Termii API Key. @@ -52,4 +61,4 @@ If you have not created one yet: - Click on the Disabled button and mark it as Active. - Click on the **Connect** button. -Now it is possible to send SMS notifications using **Termii** in Novu. \ No newline at end of file +Now it is possible to send SMS notifications using **Termii** in Novu. diff --git a/channels-and-providers/sms/twilio.mdx b/integrations/providers/sms/twilio.mdx similarity index 84% rename from channels-and-providers/sms/twilio.mdx rename to integrations/providers/sms/twilio.mdx index 4a9a4268..2978dfdb 100644 --- a/channels-and-providers/sms/twilio.mdx +++ b/integrations/providers/sms/twilio.mdx @@ -1,6 +1,6 @@ --- -title: 'Twilio' -description: 'Learn how to use the Twilio provider to send sms notifications using Novu' +title: "Twilio" +description: "Learn how to use the Twilio provider to send sms notifications using Novu" --- You can utilize the [Twilio](https://www.twilio.com/) API to communicate with your customers using SMS messaging. Let’s look at how you can do that: @@ -21,15 +21,15 @@ Irrespective of the language you use, the process is the same: - Load the Account SID and Auth token into the code, using some sort of safe environment such as .env variables. Nobody should have access to this but you. - Create a client object from Twilio that take the SID and Token as variables then create a message with the client. - A message is an object that has three things: - - that free number from earlier, - - the number you would like to send to, and - - a body message that you want to send through SMS. + - that free number from earlier, + - the number you would like to send to, and + - a body message that you want to send through SMS. # Creating a Twilio integration with Novu Creating a Twilio integration with Novu is quite simple. Just follow these steps: -- Visit the [Integrations Store](https://web.novu.co/integrations) on Novu. +- Visit the [Integrations Store](https://dashboard.novu.co/integrations) on Novu. - Click the "Add a provider" button. - Locate **Twilio** and click on the `Disabled` button and mark it as `Active`. - Click on the **Connect** button. @@ -43,14 +43,12 @@ Creating a Twilio integration with Novu is quite simple. Just follow these steps To send a WhatsApps message with Twillio integration prefix the phone number of the subscriber with `whatsapp:` as shown below. ```ts -await novu.trigger('', - { - to: { - subscriberId: '', - phone: 'whatsapp:555-4242' - }, - } -); +await novu.trigger("", { + to: { + subscriberId: "", + phone: "whatsapp:555-4242", + }, +}); ``` Read more about [sending a Message with the Twilio API for WhatsApp](https://www.twilio.com/docs/whatsapp/tutorial). diff --git a/integrations/schema/zod.mdx b/integrations/schema/zod.mdx new file mode 100644 index 00000000..d51685be --- /dev/null +++ b/integrations/schema/zod.mdx @@ -0,0 +1,54 @@ +--- +title: "Integrate Zod with your notification workflows" +sidebarTitle: "Zod" +--- + +Novu Framework allows you to use [Zod](https://zod.dev/) to define the [Control](/concepts/controls) and [Payload](/concepts/payload) schemas for your workflows. + +## Add Zod to your project + + + + ```bash + npm install zod zod-to-json-schema + ``` + + Novu requires the `zod-to-json-schema` package to generate JSON schemas from your Zod definitions. + + + After installation, the Zod schemas can be used interchangeably with the `controlSchema` and `payloadSchema` options in your workflow definitions. + + ```tsx + import { workflow } from '@novu/framework'; + import { z } from 'zod'; + + export const testWorkflow = workflow('test-workflow', async ({ step, payload }) => { + await step.email('send-email', async (controls) => { + return { + subject: controls.subject, + body: 'Hello, World!', + }; + }, + { + controlSchema: z.object({ + subject: z.string().default('A test subject'), + }), + }); + }, { + payloadSchema: z.object({ + userName: z.string(), + }), + }); + ``` + + + + +## Controls and Payload UI + +When you define a `controlSchema` for a step, Novu will automatically generate a UI for the controls in the workflow editor. + +- **Form Input Title** - Will be derived from the key of the Zod schema. Unfortunately Zod does not support custom titles at this point. +- **Form Input Type** - Will be derived from the Zod schema type, with support for `string`, `number`, `boolean`, and `enum` and `array` types. +- **Default Value** - Will be derived from the Zod schema default value. +- **Validation** - Will be derived from the Zod schema validation rules, including `min`, `max`, `email`, `url`, `regex` and etc... diff --git a/introduction.mdx b/introduction.mdx index 27f9d09b..308879d5 100644 --- a/introduction.mdx +++ b/introduction.mdx @@ -1,6 +1,6 @@ --- title: Introduction -description: 'Welcome to the home of novu documentation!' +description: "Welcome to the home of novu documentation!" --- - - - - + + {" "} + + + {" "} + + + {" "} + + + {" "} + + + {" "} + Let's build and run our iOS application to see if everything works correctly. - + + {" "} + -Go to the Firebase documentation website, go to docs here, and select Cloud Messaging. +Go to the Firebase documentation website, go to docs here, and select Cloud Messaging. We will follow this guide to set up cloud messaging inside the app. You can read more about how Firebase Cloud Messaging works by reading their [official documentation](https://firebase.google.com/docs/cloud-messaging/ios/client). - + + {" "} + Go to iOS+ and click on the `Set up an Apple platforms client` section. - + + {" "} + The first thing that we need to do is add Firebase to our iOS app. Let's create a new Firebase project inside of the console. - - - - + + {" "} + + + {" "} + + + {" "} + + + {" "} + ## Create a Firebase project Select a name for your project. - + + {" "} + Then, add Google Analytics to the Firebase project. - + + {" "} + Here, you can select your Google account. - - + + {" "} + + + {" "} + We will click on Add Firebase to your iOS app. - + + {" "} + We first need to add the bundle name, so go to your iOS Xcode project and copy and paste the bundle name. - - + + {" "} + + + {" "} + Download the `.plist` file and put it in the root of your project. - - + + {" "} + + + {" "} + You can put it right under `info.plist` file. - + + {" "} + We need to add the Firebase SDK using the Swift package manager. -Copy this https://github.com/firebase/firebase-ios-sdk URL and then go to 'File` -> `Add Package Dependencies...`. +Copy this https://github.com/firebase/firebase-ios-sdk URL and then go to 'File`->`Add Package Dependencies...`. Paste that URL in the top right. - - - - - - + + {" "} + + + {" "} + + + {" "} + + + {" "} + + + {" "} + + + {" "} + We must add the initialization code to the `AppDelegates.swift` file. @@ -102,8 +158,9 @@ Let's import the "FirebaseCore" dependency by adding `import FirebaseCore` to th Then, we will copy `firebaseapp.configure()` and place it in `didFinishLaunchingWithOptions` method. - - + + {" "} + ```Swift @@ -150,67 +207,97 @@ class AppDelegate: UIResponder, UIApplicationDelegate { Click 'Next' and continue to the console. - - + + {" "} + + + {" "} + Let's build and run our project and see if we get Firebase log messages in the console. - - - + + {" "} + + + {" "} + ## Create and upload our APNs authentication key We will create and upload our APNs authentication key to the Firebase project settings. - + + {" "} + -Navigate to [Apple Developer Member Center](https://developer.apple.com/account). +Navigate to [Apple Developer Member Center](https://developer.apple.com/account). Head to the "Keys" section under "Certificates, IDs & Profiles". - + + {" "} + Create a new key. - + + {" "} + Select "Apple Push Notification Service". - + + {" "} + Click "Register". - + + {" "} + We have to download this key and upload it into Firebase. - + + {" "} + Head to "Project Settings". - + + {" "} + Click on Cloud Messaging, then click "Upload APNs Authentication Key". - + + {" "} + Now we can upload the dots p8 file. - + + {" "} + You must enter your `Key ID` and `Team ID`, which you can find in the top right corner. - - + + {" "} + + + {" "} + -## Register for Remote Notifications +## Register for Remote Notifications - + + {" "} + 1. Copy this code block and place it inside the `AppDelegate.swift` file using the `didFinishLaunchingWithOptions` method. -We paste this code block under `firebaseApp.configure()`. - + We paste this code block under `firebaseApp.configure()`. 2. We need to conform to this delegate, so we will also create an `AppDelegate` extension at the bottom for `UNUserNotificationCenterDelegate`. @@ -237,9 +324,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. FirebaseApp.configure() - + // Register for Remote Notitifcations - + UNUserNotificationCenter.current().delegate = self let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound] @@ -250,7 +337,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { application.registerForRemoteNotifications() - + return true } @@ -272,7 +359,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } extension AppDelegate: UNUserNotificationCenterDelegate { - + } ``` @@ -281,12 +368,18 @@ extension AppDelegate: UNUserNotificationCenterDelegate { To do this, we must first set the `Messaging.messaging().delegate = self` inside the `didFinishLaunchingWithOptions` method. - - + + {" "} + + + {" "} + We will now add a 'Messaging' delegate extension to `AppDelegate.swift` file. - + + {" "} + ```Swift @@ -311,9 +404,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { FirebaseApp.configure() FirebaseConfiguration.shared.setLoggerLevel(.min) - + // Register for Remote Notitifcations - + UNUserNotificationCenter.current().delegate = self let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound] @@ -323,12 +416,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate { ) application.registerForRemoteNotifications() - + // Messaging Delegate - + Messaging.messaging().delegate = self - + return true } @@ -350,7 +443,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } extension AppDelegate: UNUserNotificationCenterDelegate { - + // Receive displayed notifications for iOS 10 devices. func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification) async @@ -382,14 +475,14 @@ extension AppDelegate: UNUserNotificationCenterDelegate { print(userInfo) } } - + extension AppDelegate: MessagingDelegate { - + func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) { print("Firebase registration token: \(String(describing: fcmToken))") - + let dataDict: [String: String] = ["token": fcmToken ?? ""] NotificationCenter.default.post( name: Notification.Name("FCMToken"), @@ -399,8 +492,8 @@ extension AppDelegate: MessagingDelegate { // TODO: If necessary send token to application server. // Note: This callback is fired at each app startup and whenever a new token is generated. } - - + + } ``` @@ -411,7 +504,9 @@ extension AppDelegate: MessagingDelegate { 2. Add the `didReceiveRemoteNotification`. 3. We must declare a `gcmMessageIDKey` inside of `AppDelegate`. We can define this as a `string` variable. - + + {" "} + ```Swift @@ -428,7 +523,7 @@ import FirebaseMessaging @main class AppDelegate: UIResponder, UIApplicationDelegate { - + let gcmMessageIDKey = "gcm.Message_ID" func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { @@ -436,9 +531,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { FirebaseApp.configure() FirebaseConfiguration.shared.setLoggerLevel(.min) - + // Register for Remote Notitifcations - + UNUserNotificationCenter.current().delegate = self let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound] @@ -448,12 +543,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate { ) application.registerForRemoteNotifications() - + // Messaging Delegate - + Messaging.messaging().delegate = self - + return true } @@ -475,7 +570,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } extension AppDelegate: UNUserNotificationCenterDelegate { - + // Receive displayed notifications for iOS 10 devices. func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification) async @@ -506,7 +601,7 @@ extension AppDelegate: UNUserNotificationCenterDelegate { // Print full message. print(userInfo) } - + func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) async -> UIBackgroundFetchResult { @@ -528,16 +623,16 @@ extension AppDelegate: UNUserNotificationCenterDelegate { return UIBackgroundFetchResult.newData } - + } - + extension AppDelegate: MessagingDelegate { - + func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) { print("Firebase registration token: \(String(describing: fcmToken))") - + let dataDict: [String: String] = ["token": fcmToken ?? ""] NotificationCenter.default.post( name: Notification.Name("FCMToken"), @@ -547,82 +642,121 @@ extension AppDelegate: MessagingDelegate { // TODO: If necessary send token to application server. // Note: This callback is fired at each app startup and whenever a new token is generated. } - - + + } ``` ## Adding app capabilities - + + {" "} + Click on the plus sign (+) and select `Background Modes`. - + + {" "} + **Select the following options:** -- Background Fetch + +- Background Fetch - Remote Notifications - Background Processing - + + {" "} + Add `Push Notifications` capabilities. - + + {" "} + ## App Build - + + {" "} + It will ask if you want to receive notifications, and we will allow it. - + + {" "} + ## Cloud Message Test In your Firebase project, navigate to 'engage' section and click on 'messaging'. - + + {" "} + Click on "Send your first message". - - + + {" "} + + + {" "} + We're going to enter the `notification title` and the `notification text`, then we're going to send the test message. - + + {" "} + We must copy and paste this FCM registration token to confirm our device (A physical or a simulator). You can find it in your Xcode console. - - - + + {" "} + + + {" "} + + + {" "} + Click on 'Test'. - + + {" "} + You should see the notification on your device! - + + {" "} + ## Novu account creation - + + {" "} + You can immediately configure FCM as a Push channel provider or navigate to the Integration Store. - + + {" "} + ## Connecting FCM as a provider - - + + {" "} + + + {" "} + -You only need to configure FCM with Novu with the Firebase Service Accounts private key. +You only need to configure FCM with Novu with the Firebase Service Accounts private key. To acquire the account key JSON file for your service account, follow this instructions: @@ -652,17 +786,27 @@ Make sure your service account key JSON content contains these fields: ``` - - - - - + + {" "} + + + {" "} + + + {" "} + + + {" "} + + + {" "} + ## Creating a workflow In Novu, creating a workflow means establishing a blueprint for sending notifications within your app. This unified structure ties together email, in-app messages, SMS, push notifications, and chat into **one entity**. -Each workflow has a unique name and identifier and includes customized content for various channels, using `{{handlebars}}` variables for personalization. +Each workflow has a unique name and identifier and includes customized content for various channels, using `{{handlebars}}` variables for personalization. This ensures consistent platform notifications and allows dynamic adjustments for individual subscribers, scenarios, and use cases. @@ -670,14 +814,22 @@ Workflow creation is for streamlining automated notifications, enabling teams to - - + + {" "} + + + {" "} + - + + {" "} + - + + {" "} + @@ -685,7 +837,9 @@ Workflow creation is for streamlining automated notifications, enabling teams to Creating a subscriber in Novu refers to the process of establishing a subscriber entity within the Novu platform. A subscriber is essentially the recipient of the notifications sent through Novu's system. When you create a subscriber, you're setting up the necessary information for Novu to send targeted notifications to that individual. - + + {" "} + @@ -694,24 +848,25 @@ Creating a subscriber in Novu refers to the process of establishing a subscriber ```JSON curl --location 'https://api.novu.co/v1/subscribers' \ - --header 'Content-Type: application/json' \ - --header 'Accept: application/json' \ - --header 'Authorization: ApiKey ' \ - --data-raw '{ - "subscriberId": "12345678", - "firstName": "Pawan", - "lastName": "Jain", - "email": "pawan.jain@domain.com", - "phone": "+1234567890", - "avatar": "avatar-url", - "locale": "en-US", - "data": { - "isDeveloper": true, - "customKey": "customValue" - } - }' -``` +--header 'Content-Type: application/json' \ + --header 'Accept: application/json' \ + --header 'Authorization: ApiKey ' \ + --data-raw '{ +"subscriberId": "12345678", +"firstName": "Pawan", +"lastName": "Jain", +"email": "pawan.jain@domain.com", +"phone": "+1234567890", +"avatar": "avatar-url", +"locale": "en-US", +"data": { +"isDeveloper": true, +"customKey": "customValue" +} +}' + +```` @@ -742,7 +897,7 @@ Creating a subscriber in Novu refers to the process of establishing a subscriber "providerId":"fcm" }' -``` +```` @@ -750,7 +905,7 @@ Creating a subscriber in Novu refers to the process of establishing a subscriber ## Sending a notification to iOS device with Novu -To send a notification with Novu, you are actually triggering a notification workflow. +To send a notification with Novu, you are actually triggering a notification workflow. You have the ability to test the trigger using the user interface or by calling Novu's API. **Trigger a workflow via the API** @@ -770,8 +925,9 @@ curl --location --request POST 'https://api.novu.co/v1/events/trigger' \ ``` - - + + {" "} + ## Dynamic Content @@ -780,9 +936,11 @@ When we were creating the first workflow, we "Hardcoded" the content of the noti Now, we will determine the content when calling the API. 1. In the workflow, we should change the values of the `title` and the `body` to `{{title}}` and `{{body}}`. -That way, we could insert values through the payload of the API call. + That way, we could insert values through the payload of the API call. - + + {" "} + 2. Add a 'payload' object to the API call. @@ -804,11 +962,13 @@ curl --location --request POST 'https://api.novu.co/v1/events/trigger' \ ``` - + + {" "} + ## Sound of a notification -The name of a sound file in your app's main bundle or in the Library/Sounds folder of your app's container directory. +The name of a sound file in your app's main bundle or in the Library/Sounds folder of your app's container directory. Specify the string "default" to play the system sound. Use this key for regular notifications. For critical alerts, use the sound dictionary instead. @@ -887,32 +1047,45 @@ curl --location --request POST 'https://api.novu.co/v1/events/trigger' \ Up to this point, your notifications have all contained only text. But if you’ve received many notifications, you know that notifications can have rich content, such as images. It’d be great if your notifications showed users a nice image related to their content. Once again, Firebase makes this super simple. -To show an image in push notifications, you’ll need to create a ***Notification Service Extension***. This is a separate target in your app that runs in the background when your user receives a push notification. The service extension can receive a notification and change its contents before iOS shows the notification to the user. -You’ll use Firebase to send an image URL inside a notification. +To show an image in push notifications, you’ll need to create a **_Notification Service Extension_**. This is a separate target in your app that runs in the background when your user receives a push notification. The service extension can receive a notification and change its contents before iOS shows the notification to the user. +You’ll use Firebase to send an image URL inside a notification. You’ll then use a content extension to download the image and add it to the notification’s content. In Xcode, go to File ▸ New ▸ Target…. Search for Notification Service Extension and select Next. Set a name and configure it to add to your main project. - - - + + {" "} + + + {" "} + + + {" "} + Select **Finish**, and when prompted, select **Activate**. - - + + {" "} + -When you added the Firebase package to your project, it was only added to the your "main" (In my case it's "PushNotificationDemo") target, so now you need to add the necessary dependency to your new extension. -Open your app’s project settings and select the name you picked for the extention under Targets. +When you added the Firebase package to your project, it was only added to the your "main" (In my case it's "PushNotificationDemo") target, so now you need to add the necessary dependency to your new extension. +Open your app’s project settings and select the name you picked for the extention under Targets. Under Frameworks and Libraries, select the + button, and search for FirebaseMessaging. Then, select Add. Your project should reflect the image below: - + + {" "} + Select the + button, and search for FirebaseMessaging. Then, select Add. - - + + {" "} + + + {" "} + Now, open `NotificationService.swift`. This file is where you can customize notifications before the user sees them. @@ -925,11 +1098,11 @@ Next, replace the contents of `didReceive(_:withContentHandler:)` with the fol self.contentHandler = contentHandler bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) - + if let bestAttemptContent = bestAttemptContent { // Modify the notification content here... bestAttemptContent.title = "\(bestAttemptContent.title)" - + // Call FIRMessaging extension helper API. if let messagingContentHandler = self.contentHandler { @@ -939,10 +1112,10 @@ Messaging.serviceExtension().populateNotificationContent(bestAttemptContent, wit ``` -Typically, you’d have to search the field containing the image URL, download the image, and finish the presentation with the picture as an attachment. +Typically, you’d have to search the field containing the image URL, download the image, and finish the presentation with the picture as an attachment. Here, you’re using Firebase’s `FIRMessagingExtensionHelper` to perform all that work automatically in a straightforward helper method call. -Remember, iOS only allows you to download your attached image. If the extension’s code takes too long to run, the system will call `serviceExtensionTimeWillExpire()`. +Remember, iOS only allows you to download your attached image. If the extension’s code takes too long to run, the system will call `serviceExtensionTimeWillExpire()`. This gives you a chance to gracefully finish up anything you are doing in the extension or to simply present the notification as is, which is the default implementation. This is the entire `NotificationService.swift` file. @@ -965,21 +1138,21 @@ class NotificationService: UNNotificationServiceExtension { var bestAttemptContent: UNMutableNotificationContent? override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { - + self.contentHandler = contentHandler bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) - + if let bestAttemptContent = bestAttemptContent { // Modify the notification content here... bestAttemptContent.title = "\(bestAttemptContent.title)" - + // Call FIRMessaging extension helper API. if let messagingContentHandler = self.contentHandler { Messaging.serviceExtension().populateNotificationContent(bestAttemptContent, withContentHandler: messagingContentHandler) } } } - + override func serviceExtensionTimeWillExpire() { // Called just before the extension will be terminated by the system. // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used. @@ -994,14 +1167,16 @@ class NotificationService: UNNotificationServiceExtension { Let's **rebuild** our app again! - + + {" "} + When making the API call, we should include: + - "mutable-content": 1 inside the "aps" object. -- "image" in "fcm_options" object, +- "image" in "fcm_options" object, - URL address of the image. - ```JSON curl --location --request POST 'https://api.novu.co/v1/events/trigger' \ @@ -1035,15 +1210,19 @@ curl --location --request POST 'https://api.novu.co/v1/events/trigger' \ ``` - - + + {" "} + + + {" "} + ## Sending actionable notifications 1. Define Notification Actions: -In your `AppDelegate.swift` file, you should define the notification actions you want to add. -You can do this by creating a `UNNotificationAction` for each action you want to include. +In your `AppDelegate.swift` file, you should define the notification actions you want to add. +You can do this by creating a `UNNotificationAction` for each action you want to include. For example, let's add two actions: "Accept" and "Reject". ```Swift @@ -1056,7 +1235,7 @@ extension UNNotificationAction { title: "Accept", options: [.foreground] ) - + static let reject = UNNotificationAction( identifier: "REJECT_ACTION", title: "Reject", @@ -1066,35 +1245,36 @@ extension UNNotificationAction { ``` -2. Register Notification Category: - - You need to register the notification category with the actions you defined in your `didFinishLaunchingWithOptions` method: +2. Register Notification Category: - ``` - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - // ... (your existing code) - - // Register notification category - let acceptAction = UNNotificationAction.accept - let rejectAction = UNNotificationAction.reject - - let messageCategory = UNNotificationCategory( - identifier: "MESSAGE_CATEGORY", - actions: [acceptAction, rejectAction], - intentIdentifiers: [], - options: [] - ) - - UNUserNotificationCenter.current().setNotificationCategories([messageCategory]) + You need to register the notification category with the actions you defined in your `didFinishLaunchingWithOptions` method: - return true -} + ``` + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // ... (your existing code) -``` + // Register notification category + let acceptAction = UNNotificationAction.accept + let rejectAction = UNNotificationAction.reject + + let messageCategory = UNNotificationCategory( + identifier: "MESSAGE_CATEGORY", + actions: [acceptAction, rejectAction], + intentIdentifiers: [], + options: [] + ) + + UNUserNotificationCenter.current().setNotificationCategories([messageCategory]) + + return true + + } + +```` 3. Handle Action Taps: -Now, you need to handle the action taps in the `userNotificationCenter(_:didReceive response:)` method of your `AppDelegate`. +Now, you need to handle the action taps in the `userNotificationCenter(_:didReceive response:)` method of your `AppDelegate`. You can check which action was tapped by inspecting the `response.actionIdentifier` property: ```Swift @@ -1117,7 +1297,7 @@ func userNotificationCenter(_ center: UNUserNotificationCenter, } } -``` +```` Your full `AppDelegate.swift` file should look like this: @@ -1136,7 +1316,7 @@ import FirebaseMessaging @main class AppDelegate: UIResponder, UIApplicationDelegate { - + let gcmMessageIDKey = "gcm.Message_ID" func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { @@ -1144,9 +1324,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { FirebaseApp.configure() FirebaseConfiguration.shared.setLoggerLevel(.min) - + // Register for Remote Notitifcations - + UNUserNotificationCenter.current().delegate = self let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound] @@ -1156,25 +1336,25 @@ class AppDelegate: UIResponder, UIApplicationDelegate { ) application.registerForRemoteNotifications() - + // Register notification category let acceptAction = UNNotificationAction.accept let rejectAction = UNNotificationAction.reject - + let messageCategory = UNNotificationCategory( identifier: "MESSAGE_CATEGORY", actions: [acceptAction, rejectAction], intentIdentifiers: [], options: [] ) - + UNUserNotificationCenter.current().setNotificationCategories([messageCategory]) - + // Messaging Delegate - + Messaging.messaging().delegate = self - + return true } @@ -1196,7 +1376,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } extension AppDelegate: UNUserNotificationCenterDelegate { - + // Receive displayed notifications for iOS 10 devices. func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification) async @@ -1218,7 +1398,7 @@ extension AppDelegate: UNUserNotificationCenterDelegate { func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async { let userInfo = response.notification.request.content.userInfo - + if response.actionIdentifier == UNNotificationAction.accept.identifier { // Handle the "Accept" action print("User tapped Accept") @@ -1239,10 +1419,10 @@ extension AppDelegate: UNUserNotificationCenterDelegate { // Print full message. print(userInfo) - - + + } - + func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) async -> UIBackgroundFetchResult { @@ -1260,22 +1440,22 @@ extension AppDelegate: UNUserNotificationCenterDelegate { // Print full message. print(userInfo) - - + + return UIBackgroundFetchResult.newData } - + } - + extension AppDelegate: MessagingDelegate { - + func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) { print("Firebase registration token: \(String(describing: fcmToken))") - + let dataDict: [String: String] = ["token": fcmToken ?? ""] NotificationCenter.default.post( name: Notification.Name("FCMToken"), @@ -1285,8 +1465,8 @@ extension AppDelegate: MessagingDelegate { // TODO: If necessary send token to application server. // Note: This callback is fired at each app startup and whenever a new token is generated. } - - + + } extension UNNotificationAction { @@ -1295,7 +1475,7 @@ extension UNNotificationAction { title: "Accept", options: [.foreground] ) - + static let reject = UNNotificationAction( identifier: "REJECT_ACTION", title: "Reject", @@ -1336,8 +1516,9 @@ curl --location --request POST "https://api.novu.co/v1/events/trigger" \ ``` - - + + {" "} + ## Additional resources diff --git a/guides/add-digest-to-email-notifications.mdx b/legacy-guides/add-digest-to-email-notifications.mdx similarity index 73% rename from guides/add-digest-to-email-notifications.mdx rename to legacy-guides/add-digest-to-email-notifications.mdx index 1f52a1e3..35f816a5 100644 --- a/guides/add-digest-to-email-notifications.mdx +++ b/legacy-guides/add-digest-to-email-notifications.mdx @@ -1,6 +1,6 @@ --- -title: 'How to Add Digest to Email Notifications' -description: 'Leverage the digest functionality to send email notifications' +title: "How to Add Digest to Email Notifications" +description: "Leverage the digest functionality to send email notifications" --- # Introduction @@ -9,9 +9,10 @@ In this guide, you’ll learn how to add digest functionality to email notificat You can find the entire code(frontend as well as backend) for this app [here](https://github.com/novuhq/digest-email-app). - -If, instead, you want to add digest to in-app notifications, we have a guide for that as well. Take a look [here](/guides/add-digest-to-inapp-notifications). + If, instead, you want to add digest to in-app notifications, we have a guide + for that as well. Take a + look [here](/guides/add-digest-to-inapp-notifications). ### What is a Digest Notification? @@ -23,7 +24,7 @@ A digest notification is a notification that consolidates information from sever ### How does Digest Notification Work? -Novu has a built-in digest engine that collects multiple trigger events, aggregates them into a single message, and delivers it to the subscriber. You can use the digest engine by adding a ‘digest node’ to your workflow in the workflow editor in the [Novu dashboard](https://web.novu.co/workflows?utm_campaign=docs-digests). If you want to learn more about it, [this](/getting-started/concepts#digest) is a great place to start. +Novu has a built-in digest engine that collects multiple trigger events, aggregates them into a single message, and delivers it to the subscriber. You can use the digest engine by adding a ‘digest node’ to your workflow in the workflow editor in the [Novu dashboard](https://dashboard.novu.co/workflows?utm_campaign=docs-digests). If you want to learn more about it, [this](/getting-started/concepts#digest) is a great place to start. Let’s see it in action now! @@ -31,7 +32,7 @@ Let’s see it in action now! To get started with this, you need the following: -- A Novu account. [Sign up for free](http://web.novu.co/?utm_campaign=docs-gs-digests) if you don’t have one yet. +- A Novu account. [Sign up for free](http://dashboard.novu.co/?utm_campaign=docs-gs-digests) if you don’t have one yet. - A working React development environment. # Workflow setup in Novu @@ -41,24 +42,42 @@ Once, you have these, follow the steps below: 1. Head over to the Novu Dashboard. 2. Click `Workflows` on the left sidebar of your Novu dashboard. 3. Click the `Create Workflow` button on the top right: - -Once you click the `create workflow` button, you’ll see a dropdown. Select `blank workflow` from the dropdown: - -You’ll now be taken to the workflow editor: - -Once here, you can add the channels you want to use for sending notifications and configure them. For this guide, we’ll use the `Email` channel. You’ll also see the `Digest` action on the right sidebar. - + + {" "} + + Once you click the `create workflow` button, you’ll see a dropdown. Select `blank + workflow` from the dropdown: + + {" "} + + You’ll now be taken to the workflow editor: + + {" "} + + Once here, you can add the channels you want to use for sending notifications + and configure them. For this guide, we’ll use the `Email` channel. You’ll also + see the `Digest` action on the right sidebar. -Each node that is added below the digest node will only be triggered after the specified time interval + Each node that is added below the digest node will only be triggered after the + specified time interval For example, in our case, say we want the email notification to be sent after every 6 hours. So, add the `digest` action below the `trigger node`, and add the `email` channel node below the `digest` node as shown below: - -The `digest` node allows you to set a specific time interval for when notifications should be sent: - -Once, you’ve configured it. Go ahead and configure the `email` channel as per your need: - + + + {" "} + +The `digest` node allows you to set a specific time interval for when notifications +should be sent: + + {" "} + +Once, you’ve configured it. Go ahead and configure the `email` channel as per your +need: + + {" "} + Here’s a brief explanation of all the options: - **1-Preview:** This shows you a glimpse of what the email notification item will look like when delivered to the client. @@ -71,7 +90,10 @@ Here’s a brief explanation of all the options: You can write your own digest template in the 'custom code' section or just follow this guide as shown in the picture above. Once you’re done configuring this to your liking, click on the `update` button on the top right. It’ll automatically create a trigger code that you can use in your app. To get it, click on the `get snippet` button on the top right and copy it: - + + + {" "} + Now, let’s see how to add Novu to our app! # Add Novu to the Backend and Configure it @@ -86,26 +108,27 @@ npm install @novu/node Now, create a route that you want to hit when called from the front end. In our demo app, this is the route: ```jsx -import express from 'express'; +import express from "express"; -import { getEmailDigest } from '../controller/emaildigest.js'; +import { getEmailDigest } from "../controller/emaildigest.js"; const router = express.Router(); -router.post('/sending-email-digest', getEmailDigest); +router.post("/sending-email-digest", getEmailDigest); export default router; ``` + Now, we need to write a controller function that will handle the logic for what is to be sent in the trigger function’s payload. In our case, this is the controller function: ```jsx -import { sendEmailDigest } from '../novu/novu.js'; +import { sendEmailDigest } from "../novu/novu.js"; export const getEmailDigest = async (req, res) => { const { notif, email } = req.body; try { await sendEmailDigest(notif, email); - res.status(201).json({ message: 'success', notif: notif }); + res.status(201).json({ message: "success", notif: notif }); } catch (error) { res.status(409).json({ message: error.message }); } @@ -116,13 +139,13 @@ To make it modular, we’ll keep the trigger code in a separate function in a se If you’re following the guide, you’ve already copied the trigger function. But before we can add it to our app, we need one key thing - Subscribers. -Subscribers are users to which the notifications are sent. You can see a list of subscribers in the [Novu dashboard](https://web.novu.co/subscribers?utm_campaign=docs-digests) as well. +Subscribers are users to which the notifications are sent. You can see a list of subscribers in the [Novu dashboard](https://dashboard.novu.co/subscribers?utm_campaign=docs-digests) as well. In our app, we’ll create a subscriber in Node.js as we’re writing our backend in Node.js, but we also have backend [SDKs](/sdks/introduction) ([Node.js](https://github.com/novuhq/novu/tree/next/packages/node), [PHP](https://github.com/novuhq/novu-php), [.NET](https://github.com/novuhq/novu-dotnet), [Elixir](https://github.com/novuhq/novu-elixir), [Go](https://github.com/novuhq/go-novu), [Ruby](https://github.com/novuhq/novu-ruby), [Python](https://github.com/novuhq/novu-python), and [Kotlin](https://github.com/novuhq/novu-kotlin)) to choose from. The recommended way to create a subscriber in NodeJS is as follows: ```jsx -await novu.subscribers.identify('digestEmailSub', { - firstName: 'digest email subscriber', +await novu.subscribers.identify("digestEmailSub", { + firstName: "digest email subscriber", email: email, }); ``` @@ -132,18 +155,18 @@ Here, we’re creating a subscriber with the `subscriberID` of `digestEmailSu Back in our app, we can now add the trigger function: ```jsx -import { Novu } from '@novu/node'; +import { Novu } from "@novu/node"; export const sendEmailDigest = async (notif, email) => { const novu = new Novu(process.env.YOUR_NOVU_API_KEY); - await novu.subscribers.identify('digestEmailSub', { - firstName: 'digest email subscriber', + await novu.subscribers.identify("digestEmailSub", { + firstName: "digest email subscriber", email: email, }); - novu.trigger('emaildigestworkflow', { + novu.trigger("emaildigestworkflow", { to: { - subscriberId: 'digestEmailSub', + subscriberId: "digestEmailSub", email: email, }, payload: { @@ -161,23 +184,23 @@ From the front end, we just need to hit the route we had defined above. Below, I ```jsx const Body = () => { - const [formInput, setFormInput] = useState({ notif: '', email: '' }); + const [formInput, setFormInput] = useState({ notif: "", email: "" }); const [buttonClicked, setButtonClicked] = useState(false); const onSubmitHandler = async (e) => { e.preventDefault(); const response = await fetch( - 'https://emaildigestbackend.onrender.com/api/v1/sending-email-digest', + "https://emaildigestbackend.onrender.com/api/v1/sending-email-digest", { - method: 'POST', + method: "POST", headers: { - 'Content-Type': 'application/json', + "Content-Type": "application/json", }, body: JSON.stringify(formInput), } ); console.log(response.data); - setFormInput({ notif: '' }); + setFormInput({ notif: "" }); }; const handleClick = () => { @@ -190,7 +213,8 @@ const Body = () => { }; const onChangeHandler = (e) => { - const value = e.target.name === 'email' ? e.target.value.trim() : e.target.value; + const value = + e.target.name === "email" ? e.target.value.trim() : e.target.value; setFormInput((prev) => ({ ...prev, @@ -201,9 +225,9 @@ const Body = () => {

    Email Digest Playground

    - Don't know how to? Start{' '} + Don't know how to? Start{" "} - {' '} + {" "} here

    @@ -247,8 +271,11 @@ We're hitting the backend route we had created earlier. The backend has been dep Congratulations on following the guide up until this point. We can style our app a little using [some TailwindCSS](https://github.com/novuhq/digest-email-app/blob/main/frontend/src/app.css). If you’ve done everything as recommended, you’ll end up with an app that looks like this: - + + + {" "} + In this app, when we enter the email id, some text in the input box, and click send, the notification appears in the inbox after the delay specified above (when we’re creating the workflow). -This is how we add digest to email notifications! \ No newline at end of file +This is how we add digest to email notifications! diff --git a/guides/add-digest-to-inapp-notifications.mdx b/legacy-guides/add-digest-to-inapp-notifications.mdx similarity index 67% rename from guides/add-digest-to-inapp-notifications.mdx rename to legacy-guides/add-digest-to-inapp-notifications.mdx index 56babcd9..08085f3f 100644 --- a/guides/add-digest-to-inapp-notifications.mdx +++ b/legacy-guides/add-digest-to-inapp-notifications.mdx @@ -1,6 +1,6 @@ --- -title: 'How to Add Digest to In-App Notifications' -description: 'Leverage the digest functionality to send in-app notifications' +title: "How to Add Digest to In-App Notifications" +description: "Leverage the digest functionality to send in-app notifications" --- ## Introduction @@ -19,7 +19,7 @@ A digest notification is a notification that consolidates information from sever ### How does Digest Notification Work? -Novu has a built-in digest engine that collects multiple trigger events, aggregates them into a single message and delivers it to the subscriber. You can use the digest engine by adding a `digest node` to your workflow in the workflow editor in the [Novu dashboard](https://web.novu.co/workflows?utm_campaign=docs-digestsinapp). +Novu has a built-in digest engine that collects multiple trigger events, aggregates them into a single message and delivers it to the subscriber. You can use the digest engine by adding a `digest node` to your workflow in the workflow editor in the [Novu dashboard](https://dashboard.novu.co/workflows?utm_campaign=docs-digestsinapp). Let’s see it in action now! @@ -27,7 +27,7 @@ Let’s see it in action now! To get started with this, you need the following: -- A Novu account. [Sign up for free](http://web.novu.co/?utm_campaign=docs-gs-digestsinapp) if you don’t have one yet. +- A Novu account. [Sign up for free](http://dashboard.novu.co/?utm_campaign=docs-gs-digestsinapp) if you don’t have one yet. - A working React development environment. ## Workflow setup in Novu @@ -37,22 +37,42 @@ Once, you have these, follow the steps below: 1. Head over to the Novu Dashboard. 2. Click `Workflows` on the left sidebar of your Novu dashboard. 3. Click the `Create Workflow` button on the top right: - -Once you click the `create workflow` button, you’ll see a dropdown. Select `blank workflow` from the dropdown: - -You’ll now be taken to the workflow editor: - -Once here, you can add the channels you want to use for sending notifications and configure them. For this guide, we’ll use the `In-App` channel. You’ll also see the `Digest` action on the right sidebar. + + {" "} + + Once you click the `create workflow` button, you’ll see a dropdown. Select `blank + workflow` from the dropdown: + + {" "} + + You’ll now be taken to the workflow editor: + + {" "} + + Once here, you can add the channels you want to use for sending notifications + and configure them. For this guide, we’ll use the `In-App` channel. You’ll also + see the `Digest` action on the right sidebar. -Each node that is added below the digest node will only be triggered after the specified time interval + Each node that is added below the digest node will only be triggered after the + specified time interval -For example, in our case, say we want the In-App notification to be sent after every 6 hours. Next, add the `digest` action below the `trigger node`, and add the `in-app` channel node below the `digest` node as shown below: - -The `digest` node allows you to set a specific time interval for when notifications should be sent: - -Once, you’ve configured it. Go ahead and configure the `in-app` channel as per your need: - +For example, in our case, say we want the In-App notification to be sent after every +6 hours. Next, add the `digest` action below the `trigger node`, and add the `in-app` +channel node below the `digest` node as shown below: + + {" "} + +The `digest` node allows you to set a specific time interval for when notifications +should be sent: + + {" "} + +Once, you’ve configured it. Go ahead and configure the `in-app` channel as per your +need: + + {" "} + Here’s a brief explanation of all the options: - **1-Preview**: This shows you a glimpse of what each notification item will look like in the Notification Center UI. @@ -63,10 +83,16 @@ Here’s a brief explanation of all the options: - **6-Filter** - This feature allows you to configure the criteria for delivering notifications. For instance, you can apply a filter based on a subscriber's online status to send them an email if they were online within the last hour. In our case, we’re going to use the following: - -Once you’re done configuring this to your liking, click on the `update` button on the top right. -It’ll automatically create a trigger code that you can use in your app. To get it, click on the `get snippet` button on the top right and copy it: - + + + {" "} + +Once you’re done configuring this to your liking, click on the `update` button on +the top right. It’ll automatically create a trigger code that you can use in your +app. To get it, click on the `get snippet` button on the top right and copy it: + + {" "} + Now, let’s see how to add Novu to our app! # Add Novu to the Backend and Configure it @@ -76,28 +102,30 @@ In your backend, install the novu package using the following code: ```bash npm install @novu/node ``` + Now, create a route which you want to hit when called from the front end. In our demo app, this is the route: ```jsx -import express from 'express'; -import { getDigest } from '../controller/digest.js'; +import express from "express"; +import { getDigest } from "../controller/digest.js"; const router = express.Router(); -router.post('/sending-digest', getDigest); +router.post("/sending-digest", getDigest); export default router; ``` Now, we need to write a controller function that will handle the logic for what is to be sent in the trigger function’s payload. In our case, this is the controller function: + ```jsx -import { sendDigest } from '../novu/novu.js'; +import { sendDigest } from "../novu/novu.js"; export const getDigest = async (req, res) => { const { name } = req.body; try { await sendDigest(name); - res.status(201).json({ message: 'success', name: name }); + res.status(201).json({ message: "success", name: name }); } catch (error) { res.status(409).json({ message: error.message }); } @@ -108,13 +136,13 @@ To make it modular, we’ll keep the trigger code in a separate function in a se If you’re following the guide, you’ve already copied the trigger function. But before we can add it to our app, we need one key thing - **Subscribers.** -Subscribers are entities to which the notifications are sent. You can see a list of subscribers in the [Novu dashboard](https://web.novu.co/subscribers?utm_campaign=docs-digests-inapp) as well. +Subscribers are entities to which the notifications are sent. You can see a list of subscribers in the [Novu dashboard](https://dashboard.novu.co/subscribers?utm_campaign=docs-digests-inapp) as well. In our app, we’ll create a subscriber in Node.js as we’re writing our backend in Node.js, but we also have backend [SDKs](/sdks/introduction) (Node.js, PHP, .NET, Go, Ruby, Python and Kotlin) to choose from. The recommended way to create a subscriber in NodeJS is as follows: ```jsx -await novu.subscribers.identify('aa234u787', { - firstName: 'digest subscriber', +await novu.subscribers.identify("aa234u787", { + firstName: "digest subscriber", }); ``` @@ -123,18 +151,18 @@ Here, we’re creating a subscriber with the `subscriberID` of `aa234u787.`  Back in our app, we can now add the trigger function: ```jsx -import { Novu } from '@novu/node'; +import { Novu } from "@novu/node"; export const sendDigest = async (name) => { const novu = new Novu(process.env.NOVU_API_KEY); - await novu.subscribers.identify('aa234u787', { - firstName: 'digest subscriber', + await novu.subscribers.identify("aa234u787", { + firstName: "digest subscriber", }); - await novu.trigger('digest-showcase', { + await novu.trigger("digest-showcase", { to: { - subscriberId: 'aa234u787', + subscriberId: "aa234u787", }, payload: { name: name, @@ -162,25 +190,27 @@ import { PopoverNotificationCenter, NotificationBell, IMessage, -} from '@novu/notification-center'; +} from "@novu/notification-center"; function Header() { function onNotificationClick(message: IMessage) { // your logic to handle the notification click if (message?.cta?.data?.url) { -window.location.href = message.cta.data.url; + window.location.href = message.cta.data.url; } } return ( - + {({ unseenCount }) => } ); } - ``` You can, of course, modify things as you need. For example, in our app, this is what we’ve done in the header: @@ -190,14 +220,14 @@ import { NovuProvider, PopoverNotificationCenter, NotificationBell, -} from '@novu/notification-center'; -import '../css/Header.css'; +} from "@novu/notification-center"; +import "../css/Header.css"; const Header = () => { function onNotificationClick(message) { // your logic to handle the notification click if (message?.cta?.data?.url) { -window.location.href = message.cta.data.url; + window.location.href = message.cta.data.url; } } return ( @@ -205,16 +235,21 @@ window.location.href = message.cta.data.url;

    Play with the digest engine!

    - Don't know how to? Start here. + Don't know how to? Start{" "} + here.

    - {({ unseenCount }) => } + {({ unseenCount }) => ( + + )}
    @@ -230,17 +265,20 @@ You’ll also need to plug in your `applicationIdentifier`. You can get your ` Beyond this, we just need to call the API we had written above when the form is submitted with the data we want in the payload. The code to do so is as follows: ```jsx -import { useState } from 'react'; -import axios from 'axios'; -import '../css/Body.css'; +import { useState } from "react"; +import axios from "axios"; +import "../css/Body.css"; const Body = () => { - const [name, setName] = useState(''); + const [name, setName] = useState(""); const onSubmitHandler = async (e) => { e.preventDefault(); - const res = await axios.post('http://localhost:3000/api/v1/sending-digest', { name }); - setName(''); + const res = await axios.post( + "http://localhost:3000/api/v1/sending-digest", + { name } + ); + setName(""); }; const onChangeHandler = (e) => { setName(e.target.value); @@ -259,8 +297,8 @@ const Body = () => { className="text" /> ); @@ -274,7 +312,12 @@ The app is done now! Congratulations for following the guide up until this point. We can style our app a little using [some CSS](https://github.com/novuhq/digest-learning-app/blob/main/frontend/src/App.css). If you’ve done everything as recommended, you’ll end up with an app that looks like this: - -In this app, when we enter some text in the input box and click send, the notification appears in the bell icon after the delay specified above (when we’re creating the workflow). -This is how we add digest to in-app notifications! \ No newline at end of file + + {" "} + +In this app, when we enter some text in the input box and click send, the notification +appears in the bell icon after the delay specified above (when we’re creating the +workflow). + +This is how we add digest to in-app notifications! diff --git a/guides/cookbook/introduction.mdx b/legacy-guides/cookbook/introduction.mdx similarity index 80% rename from guides/cookbook/introduction.mdx rename to legacy-guides/cookbook/introduction.mdx index 1ef78e8e..76eeab5d 100644 --- a/guides/cookbook/introduction.mdx +++ b/legacy-guides/cookbook/introduction.mdx @@ -5,17 +5,20 @@ title: "Recipes" This cookbook contains recipes and code samples demonstrating how to accomplish everyday tasks with Novu in your application. Each code example uses our libraries and SDKs. - + - ## Fetch Subscriber Feed A subscriber feed is a list of all In-App messages for a single subscriber. It's a continuous stream of messages displayed in a list that subscribers can scroll through on the frontend via the Notification Center. It is a dynamic list with **seen** and **unseen** capabilities. Multiple feeds can exist for a subscriber. -**Subscriber Feed** is very different from **Activity Feed**. The former is for In-App channels, while the latter is a list of every message and relevant metadata across all channels shown in your dashboard. + + **Subscriber Feed** is very different from **Activity Feed**. The former is + for In-App channels, while the latter is a list of every message and relevant + metadata across all channels shown in your dashboard. + The code sample below fetches the list of all In-App messages sent to a specific subscriber: @@ -25,14 +28,15 @@ const { data: inAppMessages } = await novu.subscribers.getNotificationsFeed('sub page: 0, limit: 10, - // it is of type string. By default all feeds messages are fetched - feedIdentifier: 'Marketing', +// it is of type string. By default all feeds messages are fetched +feedIdentifier: 'Marketing', - // seen and read filter of type boolean - seen: true, - read: true +// seen and read filter of type boolean +seen: true, +read: true }); -``` + +```` ## Fetch All Feeds @@ -42,9 +46,10 @@ In-App messages are grouped in Feeds. There can be one or multiple feeds. The code sample below fetches all the feeds that have been created and exist in the In-App steps: -```javascript Node.js +```javascript Node.js const { data: feedsData } = await novu.feeds.get(); -``` +```` + ## Delete a Message From a Feed @@ -54,9 +59,7 @@ A message is a content sent to a single subscriber over a single channel. Some m A single message can be deleted from a Feed. The code sample below shows how to do it: -```javascript Node.js -await novu.messages.deleteById('messageId'); -``` + ```javascript Node.js await novu.messages.deleteById('messageId'); ``` ## Fetch all Messages Sent To All Subscribers @@ -73,21 +76,23 @@ import { ChannelTypeEnum } from '@novu/node'; // All fields are optional const listMessagesOptions = { - // pagination options - page: 0, - limit: 20, +// pagination options +page: 0, +limit: 20, - /** - * Filter options - */ +/\*\* + +- Filter options + \*/ // use ChannelTypeEnum.PUSH for push, ChannelTypeEnum.IN_APP for in-app, channel: ChannelTypeEnum.EMAIL, // only email type messages will be fetched subscriberId: '6444105141ffb0919496dfcb', transactionIds: ['644-41051-41ffb0-919496-dfcb'], -}; + }; const { data: messagesData } = await novu.messages.list(listMessagesOptions); -``` + +```` ## Mark an In-App Message as Read/Seen @@ -95,7 +100,7 @@ const { data: messagesData } = await novu.messages.list(listMessagesOptions); You can mark an In-App message as read/seen. Messages from other channels: **Email**, **Push**, **Chat**, **Sms** can't be marked as read/seen. -```javascript Node.js +```javascript Node.js const { data: markMessageAsRead } = await novu.subscribers.markMessageRead( 'subscriberId', 'messageId' @@ -105,7 +110,8 @@ const { data: markMessageAsSeen } = await novu.subscribers.markMessageSeen( 'subscriberId', 'messageId' ); -``` +```` + ## Mark an In-App Message as Read/Unread/Seen/Unseen @@ -133,18 +139,19 @@ Messages from other channels: **Email**, **Push**, **Chat**, **Sms** can't be ma import { MarkMessagesAsEnum } from "@novu/node" const { data: markAllInAppMessages } = await novu.subscribers.markAllMessagesAs( - 'subscriberId', +'subscriberId', - // can be filtered with feed identifiers - feedIdentifier: ['Marketing', 'Product'] +// can be filtered with feed identifiers +feedIdentifier: ['Marketing', 'Product'] - // MarkMessageAsEnum.READ => It will mark all messages as read - // MarkMessageAsEnum.SEEN => It will mark all messages as seen - // MarkMessageAsEnum.UNREAD => It will mark all messages as unread - // MarkMessageAsEnum.UNSEEN => It will mark all messages as unseen - markAs: MarkMessageAsEnum.Read +// MarkMessageAsEnum.READ => It will mark all messages as read +// MarkMessageAsEnum.SEEN => It will mark all messages as seen +// MarkMessageAsEnum.UNREAD => It will mark all messages as unread +// MarkMessageAsEnum.UNSEEN => It will mark all messages as unseen +markAs: MarkMessageAsEnum.Read ); -``` + +```` ## Send Slack Notifications @@ -178,8 +185,8 @@ await novu.trigger('slack', { chatMsg: '' } }); -``` +```` where `chatMsg` is a payload variable in the workflow editor. -Follow the [full guide](/guides/slack-guide) on how to send Slack notifications using Novu. \ No newline at end of file +Follow the [full guide](/guides/slack-guide) on how to send Slack notifications using Novu. diff --git a/legacy-guides/demos/introduction.mdx b/legacy-guides/demos/introduction.mdx new file mode 100644 index 00000000..bb324d70 --- /dev/null +++ b/legacy-guides/demos/introduction.mdx @@ -0,0 +1,129 @@ +--- +title: "Demo apps" +description: "Experience the power of Novu for triggering, digesting, and receiving notifications via different channels" +--- + +### 1. Email, In-App Notification & Authentication + + + + + + + + Find the source code here! + + + Try it yourself first-hand! + + + +### 2. Headless Notification Center + + + + + + + + Find the source code here! + + + Try it yourself first-hand! + + + +### 3. Notification Center + + + + + + + + Find the source code here! + + + Try it yourself first-hand! + + + +### 4. Email Digest Engine + + + + + + + + Find the source code here! + + + Try it yourself first-hand! + + + +### 5. InApp Digest Engine + + + + + + + + Find the source code here! + + + Try it yourself first-hand! + + diff --git a/guides/discord-guide.mdx b/legacy-guides/discord-guide.mdx similarity index 50% rename from guides/discord-guide.mdx rename to legacy-guides/discord-guide.mdx index ef9c8df9..c85364f1 100644 --- a/guides/discord-guide.mdx +++ b/legacy-guides/discord-guide.mdx @@ -1,80 +1,116 @@ --- -title: 'How to use Novu to send notifications to a channel in a Discord server' -description: 'Learn to send Discord notifications swiftly with Novu' +title: "How to use Novu to send notifications to a channel in a Discord server" +description: "Learn to send Discord notifications swiftly with Novu" --- # Introduction In this guide, you'll learn how to use Novu to send notifications to any channel in a Discord server. But before coding anything up, we just need to go over a couple of setup steps. The corresponding docs for this guide are available on our [docs](https://docs.novu.co/channels-and-providers/chat/discord). -The entire code for this app is available on our [GitHub](https://github.com/novuhq/discord-chat-app). + + + The entire code for this app is available on our + [GitHub](https://github.com/novuhq/discord-chat-app). + So let's begin! # Set up Discord + Setting up Discord is fairly straightforward. You just need the `webhook Url`. It is pretty simple and can be done in the following easy steps: + 1. Go to the channel you want to add the webhook to (you need to be an admin of the discord server). - + + {" "} + 2. Right-click the channel and select “Edit Channel”. - + + {" "} + 3. Select Integrations -> Webhooks -> Create Webhook - + + {" "} + 4. Edit the Bot name to your liking, copy the webhook URL and store it somewhere. We'll need it in the future. - + + {" "} + # Set up Novu + In this part, we'll set up a workflow that will be triggered when we send a notification. Workflows are like blueprints and hold the entire flow of notifications. You can read more about them on our [docs](https://docs.novu.co/workflows/notification-workflows). To set up a notification workflow for our app, follow these steps: -1. Make sure that you've set the Discord Integration as active from the [Novu Integrations Store](https://web.novu.co/integrations?utm_campaign=docs-discordnotifications). - -2. Goto the [Novu Web Dashboard](https://web.novu.co/workflows?utm_campaign=docs-discordnotifications). + +1. Make sure that you've set the Discord Integration as active from the [Novu Integrations Store](https://dashboard.novu.co/integrations?utm_campaign=docs-discordnotifications). + + {" "} + +2. Goto the [Novu Web Dashboard](https://dashboard.novu.co/workflows?utm_campaign=docs-discordnotifications). 3. Click on the 'Add a workflow' button and select 'Blank workflow' from the dropdown. - + + {" "} + 4. Once there, give your workflow a name and drag and drop the 'chat' option below the 'workflow trigger' step. - + + {" "} + 5. You can also add variables in the Workflow Editor. For example, here I've added 'chatMsg' as a variable as I'll be sending data using it. - -Whatever is placed inside double braces is a variable. + + {" "} + + Whatever is placed inside double braces is a variable. 6. Click the 'Get Snippet' button, copy the trigger code and keep it somewhere. We'll need this to trigger this workflow. - -Now, we've completed all the setup required and can move to coding! + + {" "} + + Now, we've completed all the setup required and can move to coding! # Create the backend + The backend for this app is quite simple. Simply install the Novu package: + ```bash npm install @novu/node ``` + Now, create a route that you'll hit when called from the front end. For our demo app, this is the route I've created: + ```jsx import express from "express"; import { chatController } from "../controller/chat.js"; const router = express.Router(); -router.post("/sendChat",chatController); +router.post("/sendChat", chatController); export default router; ``` Now, we need a controller function to handle what is to be sent in the trigger's function payload. Here's the controller I wrote: + ```jsx -import { chat } from "../novu/novu.js" +import { chat } from "../novu/novu.js"; export const chatController = async (req, res) => { - const { chatMsg } = req.body; - try { - await chat(chatMsg); - res.status(201).json({ message: "Message sent successfully" }); - } catch (error) { - console.log("notifController error:", error); - res.status(500).json({ message: error.message }) - } -} + const { chatMsg } = req.body; + try { + await chat(chatMsg); + res.status(201).json({ message: "Message sent successfully" }); + } catch (error) { + console.log("notifController error:", error); + res.status(500).json({ message: error.message }); + } +}; ``` -Notice how we're expecting 'chatMsg' in our payload. This is why we added it as a variable in the workflow created earlier. -To make it modular, we’ll keep the trigger code in a separate function in a separate file, `novu.js`, in our case, which is as follows: -```jsx -import { Novu, ChatProviderIdEnum } from '@novu/node'; + + + Notice how we're expecting 'chatMsg' in our payload. This is why we added it + as a variable in the workflow created earlier. + +To make it modular, we’ll keep the trigger code in a separate function in a separate +file, `novu.js`, in our case, which is as follows: ```jsx import { + (Novu, ChatProviderIdEnum) +} from '@novu/node'; export const chat = async (chatMsg) => { try { @@ -91,11 +127,13 @@ export const chat = async (chatMsg) => { } catch (error) { console.log(error); } + } -``` + +```` Add the trigger code you'd copied earlier: ```jsx -import { Novu } from '@novu/node'; +import { Novu } from '@novu/node'; const novu = new Novu(''); @@ -107,47 +145,64 @@ novu.trigger('chat-with-discord', { chatMsg: '' } }); -``` +```` Our final trigger code should look something like this: + ```jsx -import { Novu, ChatProviderIdEnum } from '@novu/node'; +import { Novu, ChatProviderIdEnum } from "@novu/node"; export const chat = async (chatMsg) => { - try { - const novu = new Novu(process.env.YOUR_NOVU_API_KEY_HERE); + try { + const novu = new Novu(process.env.YOUR_NOVU_API_KEY_HERE); + + await novu.subscribers.identify(process.env.SUB_ID, { + firstName: "newSubForDiscordChat", + }); + + await novu.subscribers.setCredentials( + process.env.SUB_ID, + ChatProviderIdEnum.Discord, + { + webhookUrl: process.env.WEBHOOK_URL, + } + ); + + await novu.trigger("chat-with-discord", { + to: { + subscriberId: process.env.SUB_ID, + }, + payload: { + chatMsg: chatMsg, + }, + }); + } catch (error) { + console.log(error); + } +}; +``` - await novu.subscribers.identify(process.env.SUB_ID, { - firstName: 'newSubForDiscordChat', - }); + + We're keeping all the sensitive data in a `.env` file to avoid hardcoding as a + good practice + - await novu.subscribers.setCredentials(process.env.SUB_ID, ChatProviderIdEnum.Discord, { - webhookUrl: process.env.WEBHOOK_URL - }); +In this code snippet above, we're first initializing a new instance of Novu, then using it to 'identify' or [create](https://docs.novu.co/subscribers/subscribers#create-a-subscriber) a [subscriber](https://docs.novu.co/subscribers/subscribers). - await novu.trigger('chat-with-discord', { - to: { - subscriberId: process.env.SUB_ID - }, - payload: { - chatMsg: chatMsg - } - }); - } catch (error) { - console.log(error); - } -} -``` -We're keeping all the sensitive data in a `.env` file to avoid hardcoding as a good practice - -In this code snippet above, we're first initializing a new instance of Novu, then using it to 'identify' or [create](https://docs.novu.co/subscribers/subscribers#create-a-subscriber) a [subscriber](https://docs.novu.co/subscribers/subscribers). -The 'identify' method tries to find a subscriber with the given info. If it can't find any such subscriber, it creates a new subscriber with the supplied info. + + The 'identify' method tries to find a subscriber with the given info. If it + can't find any such subscriber, it creates a new subscriber with the supplied + info. + After creating the subscriber, it runs the trigger code for the workflow we had created. # Front end set up -For our demonstration purposes, the front-end is pretty basic. All we have to do is have an input field to take the notification text in and send the payload. The front-end code for this demo app is available on our [Github repo](https://github.com/novuhq/discord-chat-app/tree/main/frontend). + +For our demonstration purposes, the front-end is pretty basic. All we have to do is have an input field to take the notification text in and send the payload. The front-end code for this demo app is available on our [Github repo](https://github.com/novuhq/discord-chat-app/tree/main/frontend). And here's our app in all its glory! - + + {" "} + -This is how we send Discord notifications using Novu! \ No newline at end of file +This is how we send Discord notifications using Novu! diff --git a/guides/fcm-flutter-novu/how-to-send-push-notifications-to-flutter-apps-with-fcm-using-Novu.mdx b/legacy-guides/fcm-flutter-novu/how-to-send-push-notifications-to-flutter-apps-with-fcm-using-Novu.mdx similarity index 86% rename from guides/fcm-flutter-novu/how-to-send-push-notifications-to-flutter-apps-with-fcm-using-Novu.mdx rename to legacy-guides/fcm-flutter-novu/how-to-send-push-notifications-to-flutter-apps-with-fcm-using-Novu.mdx index e28b3552..bae862f2 100644 --- a/guides/fcm-flutter-novu/how-to-send-push-notifications-to-flutter-apps-with-fcm-using-Novu.mdx +++ b/legacy-guides/fcm-flutter-novu/how-to-send-push-notifications-to-flutter-apps-with-fcm-using-Novu.mdx @@ -1,6 +1,6 @@ --- -title: 'How to send push notifications to Flutter apps (Android & iOS) with FCM using Novu' -description: 'Learn how to integrate Firebase Cloud Messaging with Novu and send notifications to Flutter apps' +title: "How to send push notifications to Flutter apps (Android & iOS) with FCM using Novu" +description: "Learn how to integrate Firebase Cloud Messaging with Novu and send notifications to Flutter apps" --- ## Prerequisites @@ -10,7 +10,7 @@ To complete this guide, you will need the following: - [Apple Developer](https://developer.apple.com/) membership (to obtain the required permissions to send push notifications). - A machine running MacOS to work on building the Flutter app for iOS devices. - [Firebase](https://firebase.google.com/) account -- [Novu](https://web.novu.co/?utm_campaign=docs-gs-guides-fcm-flutter) account. +- [Novu](https://dashboard.novu.co/?utm_campaign=docs-gs-guides-fcm-flutter) account. - [Android Studio](https://developer.android.com/studio/install) configured with Dart and Flutter plugins. - [Xcode](https://developer.apple.com/xcode/) installed on your machine. @@ -176,6 +176,7 @@ class _MyHomePageState extends State { } } ``` + _lib/main.dart_ There are 4 sections to pay attention to: @@ -211,6 +212,7 @@ There are 4 sections to pay attention to: print('Registration Token=$token'); } ``` + This handler listens to when a push notification is sent from FCM to the device. If the app is in the foreground, then it prints out the notification title, body, messageId, and data properties to the console. @@ -227,6 +229,7 @@ There are 4 sections to pay attention to: _messageStreamController.sink.add(message); }); ``` + This handler listens to when a push notification is sent from FCM to the device. If the app is in the background, then it prints out the notification title, body, messageId, and data properties to the console. @@ -250,72 +253,94 @@ There are 4 sections to pay attention to: FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); ``` + Now, run the Flutter app. Your device should be connected to your machine to enable the app to run on it. - +
    - +
    - + - ## Cloud Message Test In your Firebase project, navigate to `Engage` section and click on `Messaging`. - + + {" "} + Click on `Create your first campaign` and select `Firebase Notification messages`. - - + + {" "} + + + {" "} + Enter the `Notification title` and the `Nabigateotification text`, and click on `Send test message`. - + + {" "} + We must copy and paste this FCM registration token to confirm our device. You can find it logged as shown earlier in our editor. - - + + {" "} + + + {" "} + Click on 'Test'. - + + {" "} + You should see the notification on your device! - + ## Sign Up on Novu -Let's use Novu to fire push notifications via FCM. First, sign up on [Novu Cloud](https://web.novu.co)?utm_campaign=docs-gs-guides-fc-flutter. +Let's use Novu to fire push notifications via FCM. First, sign up on [Novu Cloud](https://dashboard.novu.co)?utm_campaign=docs-gs-guides-fc-flutter. - + + {" "} + Next, immediately configure FCM as a Push channel provider. - + + {" "} + You can also do this by heading straight to the `Integrations Store`. Click on `Add a provider`. -## Connect FCM as a Push Channel provider +## Connect FCM as a Push Channel provider - - + + {" "} + + + {" "} + -You only need to configure FCM with Novu with the Firebase Service Accounts private key. +You only need to configure FCM with Novu with the Firebase Service Accounts private key. To acquire the account key JSON file for your service account, follow this instructions: @@ -345,30 +370,48 @@ Make sure your service account key JSON content contains these fields: ``` - - - - - + + {" "} + + + {" "} + + + {" "} + + + {" "} + + + {" "} + ## Create a Notification Workflow In Novu, creating a workflow means establishing a blueprint for sending notifications within your app. This unified structure ties together email, in-app messages, SMS, push notifications, and chat into **one entity**. -Each workflow has a unique name and identifier and includes customized content for various channels, using `{{handlebars}}` variables for personalization. +Each workflow has a unique name and identifier and includes customized content for various channels, using `{{handlebars}}` variables for personalization. This ensures consistent platform notifications and allows dynamic adjustments for individual subscribers, scenarios, and use cases. - - + + {" "} + + + {" "} + - + + {" "} + - + + {" "} + @@ -380,7 +423,9 @@ A subscriber is essentially the recipient of the notifications sent through Novu You can see the list of subscribers in the `Subscribers` section of the Novu dashboard. - + + {" "} + @@ -389,17 +434,18 @@ You can see the list of subscribers in the `Subscribers` section of the Novu das ```JSON curl --location 'https://api.novu.co/v1/subscribers' \ - --header 'Content-Type: application/json' \ - --header 'Accept: application/json' \ - --header 'Authorization: ApiKey ' \ - --data-raw '{ - "subscriberId": "584349343", - "firstName": "Flutter", - "lastName": "Boy", - "email": "flutterboy@gmail.com", - }' -``` +--header 'Content-Type: application/json' \ + --header 'Accept: application/json' \ + --header 'Authorization: ApiKey ' \ + --data-raw '{ +"subscriberId": "584349343", +"firstName": "Flutter", +"lastName": "Boy", +"email": "flutterboy@gmail.com", +}' + +```` @@ -430,8 +476,9 @@ You can see the list of subscribers in the `Subscribers` section of the Novu das "providerId":"fcm" }' - ``` - _Update the Subscriber Credentials by adding the device tokens to the subscriber_ +```` + +_Update the Subscriber Credentials by adding the device tokens to the subscriber_ @@ -439,7 +486,7 @@ You can see the list of subscribers in the `Subscribers` section of the Novu das ## Sending a Push Notification to a Device(Android/iOS) with Novu -To send a notification with Novu, you are actually triggering a notification workflow. +To send a notification with Novu, you are actually triggering a notification workflow. You have the ability to test the trigger using the Novu UI or by calling Novu's API. **Trigger a workflow via the API** @@ -463,17 +510,22 @@ curl --location --request POST 'https://api.novu.co/v1/events/trigger' \ **Trigger a workflow via the Novu Dashboard** - - + + {" "} + + + {" "} + **Receive Triggered Push Workflow on Device** - + - ## Dynamic Content When we were creating the first workflow, we "Hardcoded" the content of the notification. @@ -481,9 +533,11 @@ When we were creating the first workflow, we "Hardcoded" the content of the noti Now, we will determine the content when calling the API. 1. In the workflow, we should change the values of the `title` and the `body` to `{{title}}` and `{{body}}`. -That way, we could insert values through the payload of the API call. + That way, we could insert values through the payload of the API call. - + + {" "} + 2. Add a `payload` object to the API call. @@ -507,7 +561,9 @@ curl --location --request POST 'https://api.novu.co/v1/events/trigger' \ ``` - + + {" "} + ## Handling Data Type Messages @@ -544,17 +600,15 @@ curl --location --request POST 'https://api.novu.co/v1/events/trigger' \ ``` - + - ## Sound of a notification -You can use the name of a sound file in your app's main bundle or in the Library/Sounds folder of your app's container directory. +You can use the name of a sound file in your app's main bundle or in the Library/Sounds folder of your app's container directory. Specify the string "default" to play the system sound. Use it for regular notifications. For critical alerts, use the sound dictionary instead. - The code below works for iOS devices: ```JSON @@ -681,23 +735,20 @@ curl --location --request POST 'https://api.novu.co/v1/events/trigger' \ ``` - ## Sending Push Notification With Image Up to this point, your notifications have all contained only text. But if you’ve received many notifications, you know that notifications can have rich content, such as images. It’d be great if your notifications showed users a nice image related to their content. Once again, Firebase makes this super simple. -To show an image in push notifications, you’ll need to create a ***Notification Service Extension***. We handled it earlier in this guide during the Apple setup. - -For reiteration, follow [this guide](https://firebase.flutter.dev/docs/messaging/apple-integration#advanced-optional-allowing-notification-images -) to ensure it is properly set up for iOS devices. +To show an image in push notifications, you’ll need to create a **_Notification Service Extension_**. We handled it earlier in this guide during the Apple setup. +For reiteration, follow [this guide](https://firebase.flutter.dev/docs/messaging/apple-integration#advanced-optional-allowing-notification-images) to ensure it is properly set up for iOS devices. When making the API call, we should include: + - "mutable-content": 1 inside the “aps” object. -- "image" in "fcm_options" object, +- "image" in "fcm_options" object, - URL address of the image. - ```JSON curl --location --request POST 'https://api.novu.co/v1/events/trigger' \ @@ -731,4 +782,4 @@ curl --location --request POST 'https://api.novu.co/v1/events/trigger' \ ## Additional resources -- [Notifications Usage - Flutter Fire](https://firebase.flutter.dev/docs/messaging/notifications) \ No newline at end of file +- [Notifications Usage - Flutter Fire](https://firebase.flutter.dev/docs/messaging/notifications) diff --git a/guides/framework-guides/digest.mdx b/legacy-guides/framework-guides/digest.mdx similarity index 69% rename from guides/framework-guides/digest.mdx rename to legacy-guides/framework-guides/digest.mdx index 6644afdb..9961cb8d 100644 --- a/guides/framework-guides/digest.mdx +++ b/legacy-guides/framework-guides/digest.mdx @@ -1,5 +1,5 @@ --- -title: 'How to batch product notifications via code' +title: "How to batch product notifications via code" description: "Leverage Novu's Digest Engine to batch product notifications" --- @@ -37,43 +37,42 @@ Novu’s Digest Engine is designed to consolidate multiple trigger events into a import { Client } from "@novu/framework"; export const client = new Client({ - apiKey: '', + apiKey: "", /** * Enable this flag only during local development */ strictAuthentication: process.env.NODE_ENV !== "development", }); -export const digestWorkflow = workflow('novu-digest', - +export const digestWorkflow = workflow( + "novu-digest", + async ({ step, payload }) => { - // Add Digest Node - const digestResult = await step.digest('email-digest', async () => ({ - unit: 'seconds', + const digestResult = await step.digest("email-digest", async () => ({ + unit: "seconds", amount: 30, })); // Send a welcome email - await step.email('send-email', () => { + await step.email("send-email", () => { const resObj = { subject: `Welcome to Novu`, - body: (digestResult.events.map((event) => event.payload.text).join('\n')), - } - return resObj + body: digestResult.events.map((event) => event.payload.text).join("\n"), + }; + return resObj; }); }, { payloadSchema: { properties: { - email: { type: 'string' }, - text: { type: 'string', default: 'Sumit' }, + email: { type: "string" }, + text: { type: "string", default: "Sumit" }, }, - required: ['text'], + required: ["text"], }, } ); - ``` @@ -84,13 +83,12 @@ export const digestWorkflow = workflow('novu-digest', 2. Here’s a simple client-side app with a handler function attached to the `submit` event: ```tsx -"use client" +"use client"; import React, { useState } from "react"; export default function Home() { - - const [email, setEmail] = useState('') - const [text, setText] = useState('') + const [email, setEmail] = useState(""); + const [text, setText] = useState(""); const onClickHandler = async (e: React.FormEvent) => { try { @@ -108,23 +106,30 @@ export default function Home() { // console.log('working fine'); } catch (error) { - console.error('Error:', error); + console.error("Error:", error); } }; return ( -
    +
    onClickHandler(e)}> setEmail(e.target.value)} style={{ // Add your CSS-in-JS styles here - padding: '0.5rem', - marginBottom: '1rem', - borderRadius: '0.25rem', - border: '1px solid #ccc', - width: '100%', - boxSizing: 'border-box', + padding: "0.5rem", + marginBottom: "1rem", + borderRadius: "0.25rem", + border: "1px solid #ccc", + width: "100%", + boxSizing: "border-box", }} /> setText(e.target.value)} style={{ // Add your CSS-in-JS styles here - padding: '0.5rem', - marginBottom: '1rem', - borderRadius: '0.25rem', - border: '1px solid #ccc', - width: '100%', - boxSizing: 'border-box', + padding: "0.5rem", + marginBottom: "1rem", + borderRadius: "0.25rem", + border: "1px solid #ccc", + width: "100%", + boxSizing: "border-box", }} /> -
    - +
    ); } - ``` 3. And the corresponding route that it hits once the event fires: ```tsx -import { Novu } from '@novu/node'; +import { Novu } from "@novu/node"; -const novu = new Novu(''); +const novu = new Novu(""); export async function POST(request: Request) { - const res = await request.json(); + const res = await request.json(); - await novu.trigger('novu-digest', { - to: { - subscriberId: 'novu-sub-digest', - }, - payload: { - email: res.email, - text: res.text - }, - }); + await novu.trigger("novu-digest", { + to: { + subscriberId: "novu-sub-digest", + }, + payload: { + email: res.email, + text: res.text, + }, + }); - console.log('triggered') - return Response.json({ success: true }); + console.log("triggered"); + return Response.json({ success: true }); } - ``` That’s it! -That’s how simple it is to use Novu’s Digest Engine. Digesting notifications enhances the user experience by providing comprehensive summaries, making it easier for users to stay informed about important updates in a concise manner, without straining users with too many notifications. We have got a thorough guide on digesting notifications [in our docs](https://docs.novu.co/guides/add-digest-to-email-notifications) that you can check out as well. \ No newline at end of file +That’s how simple it is to use Novu’s Digest Engine. Digesting notifications enhances the user experience by providing comprehensive summaries, making it easier for users to stay informed about important updates in a concise manner, without straining users with too many notifications. We have got a thorough guide on digesting notifications [in our docs](https://docs.novu.co/guides/add-digest-to-email-notifications) that you can check out as well. diff --git a/guides/framework-guides/framework-mjml.mdx b/legacy-guides/framework-guides/framework-mjml.mdx similarity index 75% rename from guides/framework-guides/framework-mjml.mdx rename to legacy-guides/framework-guides/framework-mjml.mdx index 40a1516d..007da505 100644 --- a/guides/framework-guides/framework-mjml.mdx +++ b/legacy-guides/framework-guides/framework-mjml.mdx @@ -1,6 +1,6 @@ --- -title: 'How to send notifications with Next.js and MJML' -description: 'Leverage MJML package to send email notifications' +title: "How to send notifications with Next.js and MJML" +description: "Leverage MJML package to send email notifications" --- # Introduction @@ -8,6 +8,7 @@ description: 'Leverage MJML package to send email notifications' This guide will walk you through how to send notifications with MJML and Novu. You can check out the code for a [sample demo app](https://github.com/novuhq/framework-mjml). ## Pre-requisites + - A Novu account - Node installed on your machine - A working NextJS development environment @@ -22,17 +23,22 @@ This guide will walk you through how to send notifications with MJML and Novu. Y npx create-novu-app --api-key= ``` + 2. Once you execute this command, you’ll be asked to give your project a name. I’ll keep the default `my-novu-app` but you can choose your own. - + + {" "} + 3. You’ll then be asked if you want to use React-email or not. Since, we’ll be using MJML, I’m choosing the default 'No' option. - + + {" "} + 4. After this step, all the dependencies will be installed and you will be able to start using Novu Framework. - + + {" "} + 5. Once this installation is complete, simply cd into the directory and start your app using the `npm run dev` command, and your app will be served on `localhost:4000` - -Make sure that the port 4000 isn’t already being used! - +Make sure that the port 4000 isn’t already being used! You’ll now have a NextJS app running on `http://localhost:4000` and you can make changes to your app as you see fit. Let’s now move to the meaty stuff - using Novu Framework in a NextJS app and the magic of Dev Studio. @@ -41,19 +47,34 @@ You’ll now have a NextJS app running on `http://localhost:4000` and you can ma The Echo Dev Studio is a companion app to the Echo Client SDK. Its goal is to provide a local environment that lives near your code. To launch the dev studio locally you can run: `npx novu-labs@latest echo`. The Dev Studio will be started by default on port `2022`, and accessible via: `http://localhost:2022` - + + + {" "} + Here’s how the Dev Studio looks on the first run: - -You’ll notice that it asks for an `Echo endpoint` at the bottom. Novu Echo requires a single HTTP endpoint (`/api/echo` or similar) to be exposed by your application. This endpoint is used to receive events from our Worker Engine. We have more on Novu endpoint [in our docs.](https://docs.novu.co/framework/concepts/endpoint) + + + {" "} + +You’ll notice that it asks for an `Echo endpoint` at the bottom. Novu Echo requires +a single HTTP endpoint (`/api/echo` or similar) to be exposed by your application. +This endpoint is used to receive events from our Worker Engine. We have more on Novu +endpoint [in our docs.](https://docs.novu.co/framework/concepts/endpoint) -You can view the Echo Endpoint as a webhook endpoint that Novu will call when it needs to retrieve contextual information for a given subscriber and notification. + You can view the Echo Endpoint as a webhook endpoint that Novu will call when + it needs to retrieve contextual information for a given subscriber and + notification. Just enter the full URL of your Echo Endpoint. In our case, it is `http://localhost:4000/api/echo` - -Once you do, you’ll see a green checkmark alongside the URL input box and a green `connected` highlight at the top right corner. + + + {" "} + +Once you do, you’ll see a green checkmark alongside the URL input box and a green +`connected` highlight at the top right corner. ## Installing and configuring MJML @@ -66,17 +87,21 @@ npm i mjml ``` -Once installed, you need to keep in mind that, MJML doesn’t play very nice with newer web technologies such as Next.js that we’re using today. In order to overcome some hurdles, we need to use the `require` statement when importing MJML in our files, such as when defining the template. + Once installed, you need to keep in mind that, MJML doesn’t play very nice + with newer web technologies such as Next.js that we’re using today. In order + to overcome some hurdles, we need to use the `require` statement when + importing MJML in our files, such as when defining the template. 2. To write an email template, you can look over some of the examples in the MJML documentation to get inspiration from. In our case, this is the template: + ```jsx // notice the use of require statement here: -const mjml2html = require("mjml") +const mjml2html = require("mjml"); export const mjmlTemplate = (inputs: Inputs) => { - const { text, buttonText, imageUrl, buttonUrl } = inputs; - return (mjml2html(` + const { text, buttonText, imageUrl, buttonUrl } = inputs; + return mjml2html(` @@ -139,17 +164,18 @@ export const mjmlTemplate = (inputs: Inputs) => {

    - Please contact us if you have any questions. + Please contact us if you have any questions. - `)); -} + `); +}; ``` 3. And as final step, we need to define the workflow that uses the template defined above. + ```jsx import { Client, workflow } from "@novu/framework"; import { mjmlTemplate } from "./mjml"; @@ -166,7 +192,7 @@ export const emailWorkflow = workflow('mjml-email-workflow', async ({ payload, s body: mjmlTemplate(inputs).html, }; }, { - inputSchema: { + controlSchema: { type: 'object', properties: { text: { type: 'string', default: 'Welcome to our service' }, @@ -186,88 +212,119 @@ export const emailWorkflow = workflow('mjml-email-workflow', async ({ payload, s } }); ``` + Once you do this, you’ll see this workflow, the steps in the workflow, step inputs, payload variables and the rendered view of this workflow on the Echo Dev Studio: - -Here, from the Dev Studio, you or your peers can change things like the text of a button, toggle visibility of a button, static text content etc and have it synced with the cloud with the `Sync to Cloud` button. + + + {" "} + +Here, from the Dev Studio, you or your peers can change things like the text of a +button, toggle visibility of a button, static text content etc and have it synced +with the cloud with the `Sync to Cloud` button. ### Payload vs Step Inputs Notice that in the Echo dev studio above, we’ve used payload as well as step inputs. Here’s how you can decide if you need either or both: -| Payload | Step Inputs | +| Payload | Step Inputs | | -------- | ------------------------------------- | | is used for dynamic content that changes from one notification to another based on events occurring in your system. | are used for static elements or predefined options that non-technical team members can modify without altering the codebase.| -| is controlled by developers and passed dynamically through the novu.trigger method. | are defined by developers but aremeant to be utilized and modified by non-technical peers.| -| Payload examples include User ID, Post ID, Comment, Order ID, 2FA token, etc., which are likely to change with each notification. | Step Inputs examples include the text of a button, whether a section should be shown, static text content, etc., which are generally static but configurable elements.| -| Payload modifications are made in the code by developers at the time of triggering a notification. | Step Inputs can be modified directly in the UI, offering a no-code solution for non-technical team members to make changes.| +| is controlled by developers and passed dynamically through the novu.trigger method. | are defined by developers but aremeant to be utilized and modified by non-technical peers.| +| Payload examples include User ID, Post ID, Comment, Order ID, 2FA token, etc., which are likely to change with each notification. | Step Inputs examples include the text of a button, whether a section should be shown, static text content, etc., which are generally static but configurable elements.| +| Payload modifications are made in the code by developers at the time of triggering a notification. | Step Inputs can be modified directly in the UI, offering a no-code solution for non-technical team members to make changes.| | Payload data is passed during the novu.trigger method and is part of the dynamic data handling process within notification workflows.| Step Inputs are predefined in the workflow configuration and can be adjusted through the Echo Dev Studio, affecting how notifications are rendered without changing the workflow logic.| - ## Syncing with the cloud, with the click of a button Once done with the workflow, now we need to sync it to the cloud. Fortunately, Novu Framework makes it a breeze to sync changes from the local machine to the cloud and it all happens with a click of a button. -To enable our cloud environment to talk to your local Bridge instance, you need to supply an Bridge endpoint URL. This sets up a communication channel between our cloud environment and your local instance. To allow Novu to communicate with your local machine a tunnel will need to be generated. - +To enable our cloud environment to talk to your local Bridge instance, you need to supply an [Bridge Endpoint](/concepts/endpoint) URL. This sets up a communication channel between our cloud environment and your local instance. To allow Novu to communicate with your local machine a tunnel will need to be generated. The `create-novu-app` command sets up `localtunnel` for you. Running the `npm run dev` script in the project launches both the Bridge application and the tunnel solution. The tunnel URL shows up in the console output. You can also use a tool like `ngrok`: ```bash -// using ngrok -ngrok http http://localhost: +// using ngrok +ngrok http http://localhost: ``` + In our case, the app is running on port 4000 so we’ll use: + ```bash ngrok http http://localhost:4000 ``` + This will create a tunnel and you’ll see something like this in the terminal: - -Remember, the exact URL (`/api/novu` or similar) you expose in your application for handling Novu Framework requests is what you'd consider the `Bridge URL`. + + + {" "} + +Remember, the exact URL (`/api/novu` or similar) you expose in your application for +handling Novu Framework requests is what you'd consider the `Bridge URL`. This URL would be the endpoint within your application's domain where Novu's Worker Engine sends requests to fetch notification content or subscriber details dynamically. In our case, it is this: https://faec-2409-40f2-3c-3b57-400e-a7f7-1fc0-1e5.ngrok-free.app/api/echo So, we’ll enter this Bridge URL: - + + + {" "} + And create diff: - + + {" "} + ## Testing our workflow + Once you’ve synced your changes in the previous step, you’ll see a notification that says ‘Sync complete’ and you can now go to Novu Cloud using the ‘Test your workflows’ link and trigger a notification. - -You’ll see the workflow you’ve created has a blue lightning bolt icon. That icon signifies that the corresponding workflow has been created with Novu Framework: - -Simply open the workflow and you can send a test email from there. + + + {" "} + +You’ll see the workflow you’ve created has a blue lightning bolt icon. That icon +signifies that the corresponding workflow has been created with Novu Framework: + + {" "} + +Simply open the workflow and you can send a test email from there. -Make sure that all the expected payload variables and step inputs are being sent in their respective fields! + Make sure that all the expected payload variables and step inputs are being + sent in their respective fields! - + + {" "} + This is the workflow test email in my inbox: - -Once tested, you can simply have this workflow triggered whenever you want. For instance, a typical use case is to have a workflow triggered when an event occurs. To replicate it, I’ve attached a handler function that triggers this workflow when the `submit` event fires: + + {" "} + +Once tested, you can simply have this workflow triggered whenever you want. For +instance, a typical use case is to have a workflow triggered when an event +occurs. To replicate it, I’ve attached a handler function that triggers this +workflow when the `submit` event fires: Here’s a simple replication of the stipulated scenario: + ```jsx -'use client'; +"use client"; -import { useState } from 'react'; +import { useState } from "react"; export default function Home() { const [loading, setLoading] = useState(false); - const [text, setText] = useState('') - const [email, setEmail] = useState('') - + const [text, setText] = useState(""); + const [email, setEmail] = useState(""); const triggerWorkflow = async () => { setLoading(true); - const response = await fetch('/api/users', { - method: 'POST', + const response = await fetch("/api/users", { + method: "POST", headers: { - 'Content-Type': 'application/json', + "Content-Type": "application/json", }, body: JSON.stringify({ text: text, - email: email + email: email, }), }); const result = await response.json(); @@ -277,43 +334,53 @@ export default function Home() { return (

    MJML Email with Novu Framework

    - setText(e.target.value)} value={text} /> - setEmail(e.target.value)} value={email} /> + setText(e.target.value)} + value={text} + /> + setEmail(e.target.value)} + value={email} + />
    ); } - ``` + And the corresponding route it hits: + ```jsx -import { Novu } from '@novu/node'; +import { Novu } from "@novu/node"; -const novu = new Novu(''); +const novu = new Novu(""); export async function POST(request: Request) { const res = await request.json(); - await novu.trigger('mjml-email-workflow', { + await novu.trigger("mjml-email-workflow", { to: { - subscriberId: 'novu-sub-two', - email: res.email + subscriberId: "novu-sub-two", + email: res.email, }, payload: { text: res.text, }, }); - console.log('triggered') + console.log("triggered"); return Response.json({ success: true }); } ``` + So there you go! This is how you create workflows using Novu Framework and deploy your changes seamlessly to the Novu cloud. You can check out the code for a [sample demo app](https://github.com/novuhq/framework-mjml). Once you've built the workflow, you might want read one of [our other guides](/guides/framework-guides/product) on how to empower product teams to manage notification workflows. -Don’t forget to share your workflows with us and as always, hit us up on Discord with any questions you might have! \ No newline at end of file +Don’t forget to share your workflows with us and as always, hit us up on Discord with any questions you might have! diff --git a/guides/framework-guides/framework-nuxt-vuemail.mdx b/legacy-guides/framework-guides/framework-nuxt-vuemail.mdx similarity index 81% rename from guides/framework-guides/framework-nuxt-vuemail.mdx rename to legacy-guides/framework-guides/framework-nuxt-vuemail.mdx index 3d88541a..051a3524 100644 --- a/guides/framework-guides/framework-nuxt-vuemail.mdx +++ b/legacy-guides/framework-guides/framework-nuxt-vuemail.mdx @@ -1,9 +1,9 @@ --- -title: 'How to send notifications with Nuxt.js and Vuemail' -description: 'Learn how to send email notifications with Nuxt.js, Vuemail and Novu' +title: "How to send notifications with Nuxt.js and Vuemail" +description: "Learn how to send email notifications with Nuxt.js, Vuemail and Novu" --- -This guide assumes you know what [a notification workflow](https://docs.novu.co/workflows/notification-workflows) is and why you need it. It also assumes you are aware of [Nuxt](https://nuxt.com/) and [VueEmail](https://vuemail.net/). +This guide assumes you know what [a notification workflow](https://docs.novu.co/workflows/notification-workflows) is and why you need it. It also assumes you are aware of [Nuxt](https://nuxt.com/) and [VueEmail](https://vuemail.net/). Fantastic. Now that we are all on the same page let’s proceed. Novu recently released a new way of baking notification workflows into your apps via code, and it’s huge! Let me walk you through it. @@ -18,7 +18,7 @@ Still here? Good, I wrote this guide for you! ## 1. Initialize **New Nuxt Project** -We will follow the [official installation guide from Nuxt](https://nuxt.com/docs/getting-started/installation), open your terminal / IDE, and run the following command: +We will follow the [official installation guide from Nuxt](https://nuxt.com/docs/getting-started/installation), open your terminal / IDE, and run the following command: ```jsx npx nuxi@latest init @@ -32,7 +32,7 @@ npx nuxi@latest init npm install @novu/framework ``` -2. We must provide an API endpoint for the [Novu Dev Studio](https://docs.novu.co/framework/concepts/studio) to fetch our notification workflow. (Don’t worry; we guide you through all the steps.) Navigate to your app's `app/server` directory and create a new directory named `api`. +2. We must provide an API endpoint for the [Novu Dev Studio](https://docs.novu.co/framework/concepts/studio) to fetch our notification workflow. (Don’t worry; we guide you through all the steps.) Navigate to your app's `app/server` directory and create a new directory named `api`. ```jsx cd app/server @@ -40,14 +40,16 @@ cd app/server mkdir api ``` -Create a file within the `app/server/api` directory and name it `novu.ts`. Copy and paste the code snippet below: +Create a file within the `app/server/api` directory and name it `novu.ts`. Copy and paste the code snippet below: ```jsx // app/server/api/novu.ts import { serve } from "@novu/framework/nuxt"; import { client, myWorkflow } from "../novu/workflows"; -export default defineEventHandler(serve({ client: client, workflows: [myWorkflow] })); +export default defineEventHandler( + serve({ client: client, workflows: [myWorkflow] }) +); ``` 3. Now, let's create the instance where we will build and maintain our notification workflow: @@ -60,7 +62,7 @@ cd app/server mkdir novu ``` -Create a file within the `app/server/novu` directory and name it `workflows.ts`. Copy and paste the code snippet below: +Create a file within the `app/server/novu` directory and name it `workflows.ts`. Copy and paste the code snippet below: ```jsx // app/server/novu/workflows.ts @@ -126,37 +128,39 @@ Below, We can see a “plain” Client instance and shaping your desired workflo // app/server/novu/workflows.ts // This is Client instance concept - not a working instance! -import { Client, workflow } from '@novu/framework'; +import { Client, workflow } from "@novu/framework"; export const client = new Client({ /** * Disable this flag only during local development * For production this should be true */ - strictAuthentication: process.env.NODE_ENV !== "development" + strictAuthentication: process.env.NODE_ENV !== "development", }); export const myWorkflow = workflow( - '', // The Workflow name. Must be unique across your Bridge application. - // Workflow resolver, the entry-point for your Workflow steps - async ({ - // Helper function to declare your Workflow Steps. - step, - // Workflow Trigger payload - payload, - }) => { - // ...your Workflow Steps -}, { - // JSON Schema for validation and type-safety. Zod, and others coming soon. - // https://json-schema.org/draft-07/json-schema-release-notes - - // The schema for the Workflow payload passed dynamically via Novu Trigger API - // Defaults to an empty schema. - payloadSchema: { properties: { name: { type: 'string' }}}, - // The schema for the Workflow inputs passed statically via Novu Web - // Defaults to an empty schema. - inputSchema: { properties: { brandColor: { type: 'string' }}}, -}); + "", // The Workflow name. Must be unique across your Bridge application. + // Workflow resolver, the entry-point for your Workflow steps + async ({ + // Helper function to declare your Workflow Steps. + step, + // Workflow Trigger payload + payload, + }) => { + // ...your Workflow Steps + }, + { + // JSON Schema for validation and type-safety. Zod, and others coming soon. + // https://json-schema.org/draft-07/json-schema-release-notes + + // The schema for the Workflow payload passed dynamically via Novu Trigger API + // Defaults to an empty schema. + payloadSchema: { properties: { name: { type: "string" } } }, + // The schema for the Workflow inputs passed statically via Novu Web + // Defaults to an empty schema. + controlSchema: { properties: { brandColor: { type: "string" } } }, + } +); ``` We can: @@ -164,7 +168,7 @@ We can: - Select a name for our workflow. - Declare [Workflow Steps](https://docs.novu.co/framework/steps/introduction) as we see fit for our use case - Configure what could be passed in the payload of the Workflow Trigger ([payloadSchema](https://docs.novu.co/framework/concepts/inputs)) -- Configure what inputs could be passed statically via Novu Web ([inputSchema](https://docs.novu.co/framework/concepts/inputs)) +- Configure what inputs could be passed statically via Novu Web ([controlSchema](https://docs.novu.co/framework/concepts/inputs)) Note: You can create and manage as many workflows as you wish within `app/server/novu/workflows.ts`; make sure to provide each workflow with a unique name. @@ -293,41 +297,50 @@ const previewText = `Join ${props.invitedByUsername} on Vercel` Now that we have everything in our notification workflow configured along with the vue-email template we will use, it’s time to see a visual representation of what we have built. 1. If you haven’t run the development environment for your Nuxt app, now is the time. - - ```jsx - npm run dev - ``` - + + ```jsx + npm run dev + ``` + 2. Do you remember that we exposed an Bridge API endpoint in our app for Dev Studio to catch? This is where it happens. - - Run the following command in a separate terminal: - - ```jsx - npx novu-labs@latest echo - ``` - - If you’ve followed this guide, you should see this: - - - - Here is where our exact API endpoint goes. If our application runs on a different port, we should change the URL manually to point Dev Studio in the right direction. - - - - Also, if we have configured everything correctly, we should see that Dev Studio sees our workflow identifier (Workflow unique name). - - - + + Run the following command in a separate terminal: + + ```jsx + npx novu-labs@latest echo + ``` + + If you’ve followed this guide, you should see this: + + + + + + Here is where our exact API endpoint goes. If our application runs on a different port, we should change the URL manually to point Dev Studio in the right direction. + + + + + + Also, if we have configured everything correctly, we should see that Dev Studio sees our workflow identifier (Workflow unique name). + + + + + 3. Click the “View Workflow” button in the Dev Studio to see the workflow. - - We should see the following: - - - + + We should see the following: + + + + Click on the workflow step node called `send-email`. We should see a preview of our email template and the `Step Input` and `Payload` variables we have configured in the workflow schemas. - + + + 4. This is the time to adjust the email template, step input schema, or define the properties we anticipate in the payload. We have a live representation of everything. You can add more steps like In-App, SMS, and chat. @@ -336,35 +349,43 @@ Click on the workflow step node called `send-email`. We should see a preview of Having completed crafting, designing, and modifying our notification workflow to suit our requirements, we're ready to push it to Novu Cloud. There, it will handle the heavy lifting and seamlessly execute all the steps we've configured whenever we trigger the workflow. 1. Click on the `Sync to Cloud` button on the top right. - - - + + + + + 2. We will need to create a local tunnel that the Novu Cloud environment can reach for local experimentation purposes. - - - - On a separate terminal, run the following command: - - ```jsx - // Change to the port where the app is currently running - - npx localtunnel --port 3000 - ``` - - We should get something like: - - ```jsx - your url is: https://famous-pumas-clap.loca.lt - ``` - - - + + + + + + On a separate terminal, run the following command: + + ```jsx + // Change to the port where the app is currently running + + npx localtunnel --port 3000 + ``` + + We should get something like: + + ```jsx + your url is: https://famous-pumas-clap.loca.lt + ``` + + + + + 3. Click the `Create Diff` button. To push (merge) the workflow code to the cloud, an API Key from our Novu account should be added to our Framework Client instance. - - - - Let’s navigate to `../app/server/novu/workflows.ts` file and add our Novu API Key. - + + + + + + Let’s navigate to `../app/server/novu/workflows.ts` file and add our Novu API Key. + ```jsx // app/server/novu/workflows.ts // This workflow already renders vue email template. @@ -421,31 +442,31 @@ export const myWorkflow = workflow('hello-world', async ({ step, payload }) => { }, ); ``` - + We will now click the `Create Diff` button again. - + - + This is where you can review all the changes made to the workflow. Once you have verified that everything is in order, go ahead and click the `Deploy Changes` button. - + - + ## 6. Testing Our Notification Workflow - + There are many ways to test and [trigger our workflow](https://docs.novu.co/api-reference/events/trigger-event), but we'll make a `cURL` API call to Novu Cloud from our terminal in this guide. - + If we don't have any subscribers or users in our database or within our Novu Cloud organization, we'll send the test to ourselves for simplicity. - + - In the `subscriberId` key, input a random number or even our email address (as long as we do not try to assign the same ID key to another subscriber, we should be good). - For the `email` key, we will need to insert a valid email address so we can actually receive the email. - - + + **Note:** Learn more about the structure of [subscriber properties](https://docs.novu.co/subscribers/subscribers). - - We can leave the payload empty or add properties aligned with the `payloadSchema` we established in the workflow definition. - + + We can leave the payload empty or add properties aligned with the `payloadSchema` we established in the workflow definition. + ```jsx - + curl -X POST https://api.novu.co/v1/events/trigger \ -H "Authorization: ApiKey " \ -H "Content-Type: application/json" \ @@ -463,13 +484,13 @@ export const myWorkflow = workflow('hello-world', async ({ step, payload }) => { "inviteFromLocation": "New York, DC" } }' - + ``` - - Now, let’s check our email inbox. - + + Now, let’s check our email inbox. + - No longer limited by UI notification steps and rigidity. - No longer limited by notification content editors and systems. The more, the merrier! - Now an IFTTT (If-This-Then-That) pro engineer! - - Once you've built the workflow, you might want read one of [our other guides](/guides/framework-guides/product) on how to empower product teams to manage notification workflows. Have fun, and share your use case on Discord with us! \ No newline at end of file + + Once you've built the workflow, you might want read one of [our other guides](/guides/framework-guides/product) on how to empower product teams to manage notification workflows. Have fun, and share your use case on Discord with us! diff --git a/guides/framework-guides/framework-react-email.mdx b/legacy-guides/framework-guides/framework-react-email.mdx similarity index 67% rename from guides/framework-guides/framework-react-email.mdx rename to legacy-guides/framework-guides/framework-react-email.mdx index 64863cd6..428f14c0 100644 --- a/guides/framework-guides/framework-react-email.mdx +++ b/legacy-guides/framework-guides/framework-react-email.mdx @@ -1,6 +1,6 @@ --- -title: 'How to send notifications with Next.js and React email' -description: 'Leverage the React email package to send email notifications' +title: "How to send notifications with Next.js and React email" +description: "Leverage the React email package to send email notifications" --- # Introduction @@ -8,6 +8,7 @@ description: 'Leverage the React email package to send email notifications' In this guide, you’ll learn how to send email notifications using the React email package. But before we jump into it, let's first take a look at the prerequisites! ## Pre-requisites + - A Novu account - Node installed on your machine - A working NextJS development environment @@ -17,20 +18,26 @@ In this guide, you’ll learn how to send email notifications using the React em [Novu Framework](https://docs.novu.co/framework/quickstart) is a "notifications as code" approach that enables developers to define workflows as functions and integrate them with their preferred libraries for email, SMS, and chat generation. 1. To get started with Novu Framework, just run this command in your terminal, it’ll scaffold a new NextJS project with Novu Framework and we’ll be ready to roll! + ```jsx npx create-novu-app --api-key= ``` + 2. Once you execute this command, you’ll be asked to give your project a name. I’ll keep the default `my-novu-app` but you can choose your own. - + + {" "} + 3. You’ll then be asked if you want to use React-email or not. You can choose to install it at this step itself or proceed with No and then install it later. I’m choosing the default No option. - + + {" "} + 4. After this step, all the dependencies will be installed and you will be able to start using Novu Framework. - + + {" "} + 5. Once this installation is complete, simply cd into the directory and start your app using the `npm run dev` command, and your app will be served on `localhost:4000` - -Make sure that the port 4000 isn’t already being used! - +Make sure that the port 4000 isn’t already being used! You’ll now have a NextJS app running on `http://localhost:4000` and you can make changes to your app as you see fit. Let’s now move to the meaty stuff - using Novu Framework in a NextJS app and the magic of Dev Studio. @@ -39,25 +46,39 @@ You’ll now have a NextJS app running on `http://localhost:4000` and you can ma The Echo Dev Studio is a companion app to the Echo Client SDK. Its goal is to provide a local environment that lives near your code. To launch the dev studio locally you can run: `npx novu-labs@latest echo`. The Dev Studio will be started by default on port `2022`, and accessible via: `http://localhost:2022` - + + + {" "} + Here’s how the Dev Studio looks on the first run: - -You’ll notice that it asks for an `Bridge endpoint` at the bottom. Novu Framework requires a single HTTP endpoint (`/api/novu` or similar) to be exposed by your application. This endpoint is used to receive events from our Worker Engine. We have more on Bridge endpoint [in our docs.](https://docs.novu.co/framework/concepts/endpoint) + + + {" "} + +You’ll notice that it asks for an `[Bridge Endpoint](/concepts/endpoint)` at the bottom. Novu Framework +requires a single HTTP endpoint (`/api/novu` or similar) to be exposed by your application. +This endpoint is used to receive events from our Worker Engine. We have more on Bridge +endpoint [in our docs.](https://docs.novu.co/framework/concepts/endpoint) -You can view the Bridge Endpoint as a webhook endpoint that Novu will call when it needs to retrieve contextual information for a given subscriber and notification. +You can view the [Bridge Endpoint](/concepts/endpoint) as a webhook endpoint that Novu will call when it needs to retrieve contextual information for a given subscriber and notification. -Just enter the full URL of your Bridge Endpoint. In our case, it is `http://localhost:4000/api/echo` - -Once you do, you’ll see a green checkmark alongside the URL input box and a green `connected` highlight at the top right corner. +Just enter the full URL of your [Bridge Endpoint](/concepts/endpoint). In our case, it is `http://localhost:4000/api/echo` + + + {" "} + +Once you do, you’ll see a green checkmark alongside the URL input box and a green +`connected` highlight at the top right corner. ## Installing and configuring React Email -If you opted for installing React Email in our CLI set-up process, you can skip installing it again. + If you opted for installing React Email in our CLI set-up process, you can + skip installing it again. Integrating React Email with Novu in our NextJS app is quite straightforward. Following are the steps to get it installed and configured: @@ -71,6 +92,7 @@ Integrating React Email with Novu in our NextJS app is quite straightforward. Fo Once installed, proceed to write an email template in the next step 2. To write an email template, you can look over some of the examples in the [React Email documentation](https://react.email/examples) to get inspiration. In our case, this is the template: + ```jsx import { Body, @@ -234,56 +256,73 @@ export function renderReactEmail(input: any, payload: any) { ``` 3. And as final step, we need to define the workflow that uses the template defined above. + ```jsx -export const newSignup = workflow('new-signup', async ({ step, payload }) => { - // Send a welcome email - await step.email('send-email', async (inputs) => { - return { - subject: `Welcome to Novu, ${payload.username}`, - body: renderReactEmail(inputs, payload), - }; - }, { - inputSchema: { - type: "object", - properties: { - showJoinButton: { type: "boolean", default: true }, - buttonText: { type: "string", default: "Join the team" }, - userImage: { - type: "string", - default: "https://react-email-demo-bdj5iju9r-resend.vercel.app/static/vercel-user.png", - format: "uri", - }, - invitedByUsername: { type: "string", default: "Alan" }, - invitedByEmail: { - type: "string", - default: "alan.turing@example.com", - format: "email", - }, - teamName: { type: "string", default: "Team Awesome" }, - teamImage: { - type: "string", - default: "https://react-email-demo-bdj5iju9r-resend.vercel.app/static/vercel-team.png", - format: "uri", - }, - inviteLink: { - type: "string", - default: "https://vercel.com/teams/invite/foo", - format: "uri", - }, - inviteFromIp: { type: "string", default: "204.13.186.218" }, - inviteFromLocation: { - type: "string", - default: "São Paulo, Brazil", - }, +export const newSignup = workflow( + "new-signup", + async ({ step, payload }) => { + // Send a welcome email + await step.email( + "send-email", + async (inputs) => { + return { + subject: `Welcome to Novu, ${payload.username}`, + body: renderReactEmail(inputs, payload), + }; }, - }, - }); - // JSON Schema for validation and type-safety. Zod, and others coming soon. -}, { payloadSchema: { properties: { text: { type: 'string' } } } }); + { + controlSchema: { + type: "object", + properties: { + showJoinButton: { type: "boolean", default: true }, + buttonText: { type: "string", default: "Join the team" }, + userImage: { + type: "string", + default: + "https://react-email-demo-bdj5iju9r-resend.vercel.app/static/vercel-user.png", + format: "uri", + }, + invitedByUsername: { type: "string", default: "Alan" }, + invitedByEmail: { + type: "string", + default: "alan.turing@example.com", + format: "email", + }, + teamName: { type: "string", default: "Team Awesome" }, + teamImage: { + type: "string", + default: + "https://react-email-demo-bdj5iju9r-resend.vercel.app/static/vercel-team.png", + format: "uri", + }, + inviteLink: { + type: "string", + default: "https://vercel.com/teams/invite/foo", + format: "uri", + }, + inviteFromIp: { type: "string", default: "204.13.186.218" }, + inviteFromLocation: { + type: "string", + default: "São Paulo, Brazil", + }, + }, + }, + } + ); + // JSON Schema for validation and type-safety. Zod, and others coming soon. + }, + { payloadSchema: { properties: { text: { type: "string" } } } } +); ``` + Once you do this, you’ll see this workflow, the steps in the workflow, step inputs, payload variables and, the rendered view of this workflow on the Echo Dev Studio: - -Here, from the Dev Studio, you or your peers can change things like the text of a button, toggle visibility of a button, static text content, etc, and have it synced with the cloud with the `Sync to Cloud` button. + + + {" "} + +Here, from the Dev Studio, you or your peers can change things like the text of a +button, toggle visibility of a button, static text content, etc, and have it synced +with the cloud with the `Sync to Cloud` button. ### Payload vs Step Inputs @@ -304,57 +343,85 @@ Notice that in the Echo dev studio above, we’ve used payload as well as step i Once done with the workflow, now we need to sync it to the cloud. Fortunately, Novu Framework makes it a breeze to sync changes from the local machine to the cloud and it all happens with a click of a button. -To enable our cloud environment to talk to your local Echo instance, you need to supply an Echo endpoint URL. This sets up a communication channel between our cloud environment and your local instance. To allow Novu to communicate with your local machine a tunnel will need to be generated. +To enable our cloud environment to talk to your local Echo instance, you need to supply an Echo endpoint URL. This sets up a communication channel between our cloud environment and your local instance. To allow Novu to communicate with your local machine a tunnel will need to be generated. The `create-novu-app` command sets up `localtunnel` for you. Running the `npm run dev` script in the project launches both the Bridge application and the tunnel solution. The tunnel URL shows up in the console output. You can also use a tool like `ngrok`: + ```bash -// using ngrok -ngrok http http://localhost: +// using ngrok +ngrok http http://localhost: ``` + In our case, the app is running on port 4000 so we’ll use: + ```bash ngrok http http://localhost:4000 ``` + This will create a tunnel and you’ll see something like this in the terminal: - -Remember, the exact URL (`/api/novu` or similar) you expose in your application for handling Novu Framework requests is what you'd consider the `Bridge URL`. + + + {" "} + +Remember, the exact URL (`/api/novu` or similar) you expose in your application for +handling Novu Framework requests is what you'd consider the `Bridge URL`. This URL would be the endpoint within your application's domain where Novu's Worker Engine sends requests to fetch notification content or subscriber details dynamically. In our case, it is this: `https://0536-2409-40f2-1039-43e1-3053-d98e-c276-ee5.ngrok-free.app/api/echo` So, we’ll enter this Bridge URL: - + + + {" "} + And create diff: - + + {" "} + ## Testing our workflow + Once you’ve synced your changes in the previous step, you’ll see a notification that says ‘Sync complete’ and you can now go to Novu Cloud using the ‘Test your workflows’ link and trigger a notification. - -You’ll see the workflow you’ve created has a blue lightning bolt icon. That icon signifies that the corresponding workflow has been created with Novu Framework: - -Simply open the workflow and you can send a test email from there. + + + {" "} + +You’ll see the workflow you’ve created has a blue lightning bolt icon. That icon +signifies that the corresponding workflow has been created with Novu Framework: + + {" "} + +Simply open the workflow and you can send a test email from there. -Make sure that all the expected payload variables and step inputs are being sent in their respective fields! + Make sure that all the expected payload variables and step inputs are being + sent in their respective fields! - + + {" "} + This is the workflow test email in my inbox: - -Once tested, you can simply have this workflow triggered whenever you want. For instance, a typical use case is to have a workflow triggered when an event occurs. To replicate it, I’ve attached a handler function that triggers this workflow when the `submit` event fires: + + {" "} + +Once tested, you can simply have this workflow triggered whenever you want. For +instance, a typical use case is to have a workflow triggered when an event +occurs. To replicate it, I’ve attached a handler function that triggers this +workflow when the `submit` event fires: Here’s a simple replication of the stipulated scenario: + ```jsx -"use client" +"use client"; import Image from "next/image"; import styles from "./page.module.css"; import React, { useState } from "react"; import axios from "axios"; export default function Home() { - - const [email, setEmail] = useState('') - const [username, setUsername] = useState('') + const [email, setEmail] = useState(""); + const [username, setUsername] = useState(""); const onClickHandler = async (e: React.FormEvent) => { try { @@ -372,23 +439,30 @@ export default function Home() { // console.log('working fine'); } catch (error) { - console.error('Error:', error); + console.error("Error:", error); } }; return ( -
    +
    onClickHandler(e)}> setEmail(e.target.value)} style={{ // Add your CSS-in-JS styles here - padding: '0.5rem', - marginBottom: '1rem', - borderRadius: '0.25rem', - border: '1px solid #ccc', - width: '100%', - boxSizing: 'border-box', + padding: "0.5rem", + marginBottom: "1rem", + borderRadius: "0.25rem", + border: "1px solid #ccc", + width: "100%", + boxSizing: "border-box", }} /> setUsername(e.target.value)} style={{ // Add your CSS-in-JS styles here - padding: '0.5rem', - marginBottom: '1rem', - borderRadius: '0.25rem', - border: '1px solid #ccc', - width: '100%', - boxSizing: 'border-box', + padding: "0.5rem", + marginBottom: "1rem", + borderRadius: "0.25rem", + border: "1px solid #ccc", + width: "100%", + boxSizing: "border-box", }} /> -
    - +
    + + )} + + or copy and paste this URL into your browser:{" "} + + {inviteLink} + + +
    + + This invitation was intended for{" "} + {username}. This invite was + sent from {inviteFromIp}{" "} + located in{" "} + {inviteFromLocation}. If you + were not expecting this invitation, you can ignore this email. If + you are concerned about your account's safety, please reply to + this email to get in touch with us. + + + + + + ); +}; + +VercelInviteUserEmail.PreviewProps = { + username: "alanturing", + userImage: `${baseUrl}/static/vercel-user.png`, + invitedByUsername: "Alan", + invitedByEmail: "alan.turing@example.com", + teamName: "Enigma", + teamImage: `${baseUrl}/static/vercel-team.png`, + inviteLink: "https://vercel.com/teams/invite/foo", + inviteFromIp: "204.13.186.218", + inviteFromLocation: "São Paulo, Brazil", +} as VercelInviteUserEmailProps; + +export default VercelInviteUserEmail; + +export function renderEmail(input: any, payload: any) { + return render(); +} +``` + +### 4. Create a Novu Workflow + +Next, create a Novu workflow with an email step. This code-first notification workflow approach makes it easy for product teams to modify notification content. + +Within the `app` directory, create an `novu` folder and add a `workflows.ts` file to it. Copy/paste the code below to the recently created file. + +```jsx +// app/novu/workflows.ts + +import { Client, workflow } from "@novu/framework"; +import { renderEmail } from "~/emails/vercel-invite-user"; + +export const client = new Client({ + apiKey: process.env.NOVU_API_KEY, + /** + * Disable this flag only during local development + * For production this should be true + */ + strictAuthentication: process.env.NODE_ENV !== "development", +}); + +export const signUpWorkflow = workflow( + "new-signup", + async ({ step, payload }) => { + // Send a welcome email + await step.email( + "send-email", + async (inputs) => { + return { + subject: `Welcome to sending emails with Novu & Remix`, + body: renderEmail(inputs, payload), + }; + }, + { + controlSchema: { + type: "object", + properties: { + showJoinButton: { type: "boolean", default: true }, + buttonText: { type: "string", default: "Join the team" }, + userImage: { + type: "string", + default: + "https://react-email-demo-bdj5iju9r-resend.vercel.app/static/vercel-user.png", + format: "uri", + }, + invitedByUsername: { type: "string", default: "Alan" }, + invitedByEmail: { + type: "string", + default: "alan.turing@example.com", + format: "email", + }, + teamName: { type: "string", default: "Team Awesome" }, + teamImage: { + type: "string", + default: + "https://react-email-demo-bdj5iju9r-resend.vercel.app/static/vercel-team.png", + format: "uri", + }, + inviteLink: { + type: "string", + default: "https://vercel.com/teams/invite/foo", + format: "uri", + }, + inviteFromIp: { type: "string", default: "204.13.186.218" }, + inviteFromLocation: { + type: "string", + default: "São Paulo, Brazil", + }, + }, + }, + } + ); + // JSON Schema for validation and type-safety. Zod, and others coming soon. + }, + { payloadSchema: { properties: { text: { type: "string" } } } } +); +``` + +### 5. Preview Email Workflow & Sync to Novu Cloud + +Open Novu Dev Studio to preview and make changes to the email workflow as needed via the command below: + +```jsx +npx novu-labs@latest echo +``` + +1. Run the Studio + + {" "} + + +**Note:** Use the port on which your Remix app is running for the [Bridge Endpoint](/concepts/endpoint) so that the Novu Dev Studio can connect to your API route as highlighted in the image above. + +2. Check out the signup email workflow and test + + + {" "} + + +3. Deploy to Novu Cloud when you're done. + +On the top right(as seen in the image above) of the Novu Dev Studio, you can sync to Novu Cloud when you're done working locally. + +**Note:** You'll need to create a local tunnel that the Novu Cloud environment can reach for local experimentation purposes. Ngrok is a good tunnel. + +### 6. Send a Notification + +Trigger a notification using the recently deployed workflow either via your [Novu Cloud dashboard](https://dashboard.novu.co) or [code](/quickstarts/react#trigger-a-notification). + +```js +import { Novu } from "@novu/node"; + +const novu = new Novu(""); + +novu.trigger("new-signup", { + to: { + subscriberId: "789", + }, +}); +``` + +Once you've built the workflow, you might want read one of [our other guides](/guides/framework-guides/product) on how to empower product teams to manage notification workflows. diff --git a/guides/framework-guides/framework-svelte.mdx b/legacy-guides/framework-guides/framework-svelte.mdx similarity index 69% rename from guides/framework-guides/framework-svelte.mdx rename to legacy-guides/framework-guides/framework-svelte.mdx index 4a28e0ee..2e540f83 100644 --- a/guides/framework-guides/framework-svelte.mdx +++ b/legacy-guides/framework-guides/framework-svelte.mdx @@ -1,6 +1,6 @@ --- -title: 'How to send notifications with Svelte and Svelte email' -description: 'Learn how to send email notifications with Svelte, Svelte email and Novu' +title: "How to send notifications with Svelte and Svelte email" +description: "Learn how to send email notifications with Svelte, Svelte email and Novu" --- # Introduction @@ -8,6 +8,7 @@ description: 'Learn how to send email notifications with Svelte, Svelte email an Learn how to send notifications with Svelte, Svelte email and Novu. You can check out the complete code for a [working app](https://github.com/novuhq/novu-svelte-email). ## Prerequisites + - A Novu account - Node installed on your machine - A working SvelteKit app @@ -24,19 +25,21 @@ Learn how to send notifications with Svelte, Svelte email and Novu. You can chec Within the `src/routes` directory, create an `api/novu` folder and add a `+server.ts` file to it. - ```jsx // src/routes/api/novu/+server.ts -import { svelteWorkflow, client } from '$lib/novu/workflows'; -import { serve } from '@novu/framework/sveltekit'; +import { svelteWorkflow, client } from "$lib/novu/workflows"; +import { serve } from "@novu/framework/sveltekit"; -export const { GET, POST, PUT } = serve({ client: client, workflows: [svelteWorkflow] }); +export const { GET, POST, PUT } = serve({ + client: client, + workflows: [svelteWorkflow], +}); ``` ### 3. Create an email template in Svelte -Within the `src/lib` directory, create an `emails` folder and add an email template in a `.svelte` file to it. +Within the `src/lib` directory, create an `emails` folder and add an email template in a `.svelte` file to it. For example, `airbnb-review.svelte`: @@ -202,6 +205,7 @@ For example, `airbnb-review.svelte`: ``` + ### 4. Create a Novu Workflow Next, create a Novu workflow with an email step. This code-first notification workflow approach makes it easy for product teams to modify notification content. @@ -209,12 +213,11 @@ Next, create a Novu workflow with an email step. This code-first notification wo ```jsx // src/lib/novu/workflows.ts -import { Client, workflow } from '@novu/framework'; -import { render } from 'svelte-email'; -import AirbnbReview from '$lib/emails/airbnb-review.svelte'; +import { Client, workflow } from "@novu/framework"; +import { render } from "svelte-email"; +import AirbnbReview from "$lib/emails/airbnb-review.svelte"; export const client = new Client({ - /** * The Novu API key is needed for when you * need to sync to Novu Cloud @@ -224,55 +227,61 @@ export const client = new Client({ * Disable this flag only during local development * For production this should be true */ - strictAuthentication: process.env.NODE_ENV !== "development" + strictAuthentication: process.env.NODE_ENV !== "development", }); -export const svelteWorkflow = workflow('airbnb-review', async ({ step, payload }) => { +export const svelteWorkflow = workflow( + "airbnb-review", + async ({ step, payload }) => { await step.email( - 'send-email', - async (inputs) => { - const html = render({ - template: AirbnbReview, - props: { - authorName: inputs.authorName, - showFeedbackButton: inputs.showFeedbackButton, - showLogoImage: inputs.showLogoImage, - feedbackButtonText: inputs.feedbackButtonText, - bottomAddress: inputs.bottomAddress, - authorImage: inputs.authorImage, - logoImage: inputs.logoImage - }, - }); - return { - subject: `Here's Your Host Feedback`, - body: html, - } - }, { - inputSchema: { - type: "object", - properties: { - showFeedbackButton: { type: "boolean", default: true }, - showLogoImage: { type: "boolean", default: true }, - authorName: { type: "string", default: "Alex" }, - feedbackButtonText: { type: "string", default: "Send My Feedback" }, - authorImage: { - type: "string", - default: "https://react-email-demo-bdj5iju9r-resend.vercel.app/static/vercel-user.png", - format: "uri", - }, - logoImage: { - type: "string", - default: "https://svelte-email-rjaapma15-konzeptfabrik.vercel.app/airbnb-logo.png", - format: "uri", - }, - bottomAddress: { - type: "string", - default: "Airbnb, Inc., 888 Brannan St, San Francisco, CA 94103", - }, - }, - }, + "send-email", + async (inputs) => { + const html = render({ + template: AirbnbReview, + props: { + authorName: inputs.authorName, + showFeedbackButton: inputs.showFeedbackButton, + showLogoImage: inputs.showLogoImage, + feedbackButtonText: inputs.feedbackButtonText, + bottomAddress: inputs.bottomAddress, + authorImage: inputs.authorImage, + logoImage: inputs.logoImage, + }, }); - } + return { + subject: `Here's Your Host Feedback`, + body: html, + }; + }, + { + controlSchema: { + type: "object", + properties: { + showFeedbackButton: { type: "boolean", default: true }, + showLogoImage: { type: "boolean", default: true }, + authorName: { type: "string", default: "Alex" }, + feedbackButtonText: { type: "string", default: "Send My Feedback" }, + authorImage: { + type: "string", + default: + "https://react-email-demo-bdj5iju9r-resend.vercel.app/static/vercel-user.png", + format: "uri", + }, + logoImage: { + type: "string", + default: + "https://svelte-email-rjaapma15-konzeptfabrik.vercel.app/airbnb-logo.png", + format: "uri", + }, + bottomAddress: { + type: "string", + default: "Airbnb, Inc., 888 Brannan St, San Francisco, CA 94103", + }, + }, + }, + } + ); + } ); ``` @@ -285,13 +294,17 @@ npx novu-labs@latest echo ``` 1. Run the Studio - + + {" "} + **Note:** Use the port on which your Svelte app is running for the Bridge endpoint so that the Novu Dev Studio can connect to your API route. 2. Check out the email workflow and test - + + {" "} + 3. Deploy to Novu Cloud when you're done. @@ -301,6 +314,6 @@ On the top right of the Novu Dev Studio, you can sync to Novu Cloud when you're ### 6. Send a Notification -Trigger a notification using the recently deployed workflow either via your [Novu Cloud dashboard](https://web.novu.co) or [code](/quickstarts/react#trigger-a-notification). +Trigger a notification using the recently deployed workflow either via your [Novu Cloud dashboard](https://dashboard.novu.co) or [code](/quickstarts/react#trigger-a-notification). Once you've built the workflow, you might want read one of [our other guides](/guides/framework-guides/product) on how to empower product teams to manage notification workflows. diff --git a/legacy-guides/framework-guides/otp.mdx b/legacy-guides/framework-guides/otp.mdx new file mode 100644 index 00000000..d62e644d --- /dev/null +++ b/legacy-guides/framework-guides/otp.mdx @@ -0,0 +1,330 @@ +--- +title: "How to send OTP verification email notifications with React-email" +description: "Learn how to send OTP verification email notifications with React-email in your NextJS app" +--- + +## Introduction + +In this guide, you’ll learn how to send OTP verification email notifications using the React-email package. Follow these steps: + +## Getting started + +Integrating Novu’s code-first workflow with React.Email for your Next.js application can be done in a few steps: + +1. Create a NextJS app and wait for the installation: + +```bash +npx create-novu-app@latest --api-key= + +// The command will ask you if you want to include react-email into your new project. +``` + +2. Once this installation is complete, simply `cd` into the directory and start your app using the `npm run dev` command. + +## Using a code-first workflow + +1. Write an email template - To write an email template, you can look over some of the examples in the [React Email](https://react.email/examples) documentation to get inspiration. In our case, this is the template: + +```ts +import { + Body, + Button, + Container, + Head, + Heading, + Hr, + Html, + Img, + Link, + Preview, + Section, + Text, + render, +} from "@react-email/components"; +import * as React from "react"; + +interface LinearLoginCodeEmailProps { + validationCode?: string; + showJoinButton?: boolean; + buttonText?: string; + inviteLink?: string; + logoURL?: string; + inviteFromLocation?: string; + inviteFromIp?: string; + supportEmail?: string; +} + +export const LinearLoginCodeEmail = ({ + validationCode, + showJoinButton, + buttonText, + inviteLink, + logoURL, + inviteFromIp, + inviteFromLocation, + supportEmail, +}: LinearLoginCodeEmailProps) => ( + + + Your login code for Linear + + + Linear + Your login code for Linear +
    + {showJoinButton && ( +
    + +
    + )} +
    + + This link and code will only be valid for the next 5 minutes. If the + link does not work, you can use the login verification code directly: + +
    + {validationCode}110658 +
    +
    + Not expecting this email? + + This invite was sent from{" "} + {inviteFromIp} located in{" "} + {inviteFromLocation}. + + + Please contact{" "} + + {supportEmail} + {" "} + if you did not request this code. + +
    + + +); + +LinearLoginCodeEmail.PreviewProps = { + validationCode: "tt226-5398x", +} as LinearLoginCodeEmailProps; + +export default LinearLoginCodeEmail; + +const logo = { + borderRadius: 21, + width: 42, + height: 42, +}; + +const main = { + backgroundColor: "#ffffff", + fontFamily: + '-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif', +}; + +const container = { + margin: "0 auto", + padding: "20px 0 48px", + maxWidth: "560px", +}; + +const secondary = { + color: "#000", + display: "inline-block", + fontFamily: "HelveticaNeue-Medium,Helvetica,Arial,sans-serif", + fontSize: "20px", + fontWeight: 500, + lineHeight: "24px", + marginBottom: "0", + marginTop: "2rem", + textAlign: "center" as const, +}; + +const paragraphSupportText = { + fontSize: "15px", + fontWeight: "700", +}; + +const paragraph = { + margin: "0 0 15px", + fontSize: "15px", + lineHeight: "1.4", + color: "#3c4149", +}; + +const buttonContainer = { + padding: "27px 0 27px", +}; + +const button = { + backgroundColor: "#5e6ad2", + borderRadius: "3px", + fontWeight: "600", + color: "#fff", + fontSize: "15px", + textDecoration: "none", + textAlign: "center" as const, + display: "block", + padding: "11px 23px", +}; + +const hr = { + borderColor: "#dfe1e4", + margin: "42px 0 26px", +}; + +const code = { + color: "#000", + display: "inline-block", + fontFamily: "HelveticaNeue-Bold", + fontSize: "16px", + fontWeight: 700, + letterSpacing: "6px", + lineHeight: "40px", + paddingBottom: "8px", + paddingTop: "8px", + margin: "0 auto", + width: "100%", + textAlign: "center" as const, +}; + +const paragraphSupport = { + color: "#444", + fontSize: "15px", + fontFamily: "HelveticaNeue,Helvetica,Arial,sans-serif", + letterSpacing: "0", + lineHeight: "23px", + padding: "0 40px", + margin: "0", + textAlign: "center" as const, +}; + +const link = { + color: "#444", + textDecoration: "underline", +}; + +const codeContainer = { + background: "rgba(0,0,0,.05)", + borderRadius: "4px", + margin: "16px auto 14px", + verticalAlign: "middle", + width: "280px", +}; + +export function renderOTPEmail(payload: any) { + return render(); +} +``` + +2. Launch Dev Studio - Novu’s code-first approach lets you see how the template would look when rendered, right when defining it, using the Dev Studio. To launch the dev studio locally you can run `npx novu-labs@latest echo`. The Dev Studio will be started by default on port 2022, and accessible via: http://localhost:2022 + + + {" "} + + +3. Define a workflow that uses that template to send notifications - In this step, we need to define a workflow that uses the template we wrote above to render the email notification: + +```ts +import { Client, workflow } from "@novu/framework"; +import { renderOTPEmail } from "./otp"; + +export const client = new Client({ + apiKey: process.env.NOVU_API_KEY, + /** + * Disable this flag only during local development + */ + strictAuthentication: process.env.NODE_ENV !== "development", +}); + +export const otpFlow = workflow( + "otp-flow", + async ({ step, payload }) => { + // Send a welcome email + await step.email( + "send-email", + async (inputs) => { + return { + subject: `Here's your verification code, Sumit!`, + body: renderOTPEmail(inputs, payload), + }; + }, + { + controlSchema: { + type: "object", + properties: { + showJoinButton: { type: "boolean", default: true }, + buttonText: { type: "string", default: "Login to Linear" }, + logoURL: { + type: "string", + default: + "https://react-email-demo-7qy8spwep-resend.vercel.app/static/linear-logo.png", + format: "uri", + }, + supportEmail: { type: "string", default: "support@linear.co" }, + validationCode: { type: "string", default: "tt226-5398x" }, + inviteLink: { + type: "string", + default: "https://linear.app", + format: "uri", + }, + inviteFromIp: { type: "string", default: "204.13.186.218" }, + inviteFromLocation: { + type: "string", + default: "São Paulo, Brazil", + }, + }, + }, + } + ); + // JSON Schema for validation and type-safety. Zod, and others coming soon. + }, + { payloadSchema: { properties: { text: { type: "string" } } } } +); +``` + +4. Triggering the workflow - Lastly, we need to trigger the workflow we created above. Here’s how to trigger it: + +```ts +import { Novu } from "@novu/node"; + +const novu = new Novu(""); + +export async function POST(request: Request) { + const res = await request.json(); + + await novu.trigger("otp-flow", { + to: { + subscriberId: "new-user", + }, + payload: { + email: res.email, + username: res.username, + }, + }); + + console.log("triggered"); + return Response.json({ success: true }); +} +``` + +When we trigger this workflow, here’s the email received on the client-side: + + + {" "} + +That’s it! + +That’s how you create and use an OTP workflow. You can check out [our docs](https://docs.novu.co/guides/framework-guides/framework-react-email) for a hands on guide with more in-depth instructions. + +Once you've built the workflow, you might want read one of [our other guides](/guides/framework-guides/product) on how to empower product teams to manage notification workflows. + +Don’t forget to share your workflows with us and as always, hit us up on Discord with any questions you might have! diff --git a/guides/framework-guides/product.mdx b/legacy-guides/framework-guides/product.mdx similarity index 75% rename from guides/framework-guides/product.mdx rename to legacy-guides/framework-guides/product.mdx index ed826d77..ac06c0a6 100644 --- a/guides/framework-guides/product.mdx +++ b/legacy-guides/framework-guides/product.mdx @@ -1,6 +1,6 @@ --- -title: 'Empowering Product Teams to Manage Notification Workflows' -description: 'Enabling product teams to independently manage and optimize notification workflows for improved efficiency and user experience' +title: "Empowering Product Teams to Manage Notification Workflows" +description: "Enabling product teams to independently manage and optimize notification workflows for improved efficiency and user experience" --- # Introduction @@ -8,30 +8,48 @@ description: 'Enabling product teams to independently manage and optimize notifi Novu’s code-first workflow empowers product teams to manage notification for end-users independently once the engineering teams have built and delivered the notification platform for the product. Let’s see how product teams can leverage it: + 1. **Engineering Team sets things up:** Engineering teams integrate Novu’s code-first workflows into their application. This involves setting up the structure of notification workflows in the codebase, defining events, steps, inputs, payload, etc. These workflows are functions that execute business logic and need to be set only once. -2. **Empowering Non-Technical Teams:** Once the foundational workflows are established, product teams can take over! Code-first workflows allow non-technical users to modify workflows safely without breaking critical integrations. Through an exposed Inputs interface, product teams can adjust notification content and behaviour, tailoring the user experience without needing to dive into the code at all. This helps both teams win - product teams are empowered to manage and optimize notifications independently, creating a dynamic and user-centric notification system without constant reliance on engineering teams. +2. **Empowering Non-Technical Teams:** Once the foundational workflows are established, product teams can take over! Code-first workflows allow non-technical users to modify workflows safely without breaking critical integrations. Through an exposed Inputs interface, product teams can adjust notification content and behaviour, tailoring the user experience without needing to dive into the code at all. This helps both teams win - product teams are empowered to manage and optimize notifications independently, creating a dynamic and user-centric notification system without constant reliance on engineering teams. 3. **Insights and Optimization**: Further, with Novu’s Web Dashboard, Product teams gain valuable insights into last-mile delivery from the activity monitor. This enables them to refine notification strategies and optimize for better user satisfaction. This collaborative approach between the Engineering and Product teams allows both teams to focus on their strengths, resulting in a more efficient and effective notification strategy, without constant interruptions in each other’s flow. ## Customising the workflow + To demonstrate the customizability, we’ll assume that the Engineering team has successfully delivered the basic setup as outlined in [one of our guides.](https://docs.novu.co/guides/framework-guides/framework-react-email) -1. To start things off, you only need to visit the workflow section in our [Web Dashboard](https://web.novu.co/). - +1. To start things off, you only need to visit the workflow section in our [Web Dashboard](https://dashboard.novu.co/). + + {" "} + + - You’ll see the blue lightning bolt icon right next to the workflow name symbolising that it is a code-first workflow. - Now, simply click on the workflow to open it and make your desired changes. - The green `Sync` indicator on the top right corner means that your app is successfully connected with Novu. + 2. Once you open a workflow, you’ll see all the steps of the workflow: - + + {" "} + 3. Simply, select a node to see the rendered view. This is what will get sent to the end user. - + + {" "} + 4. To start making changes, simply click on either of the two buttons highlighted below: - + + {" "} + 5. Once you click on it, you’ll be taken to the edit view where you’ll see real-time preview of the workflow. You can make changes to the step inputs on the right-hand side and the rendered view will update in real-time to reflect those changes - + + {" "} + 6. Let's take a look at what payload and step inputs are: 7. In almost all the scenarios, you’ll be tweaking and modifying step inputs and the workflow editor makes it blazing fast and effortlessly simple. Simply change the text of the respective field or button or toggle any button on or off till the rendered view becomes what you want your end users to receive. Here’s how you can tweak it: - + + {" "} + + ## Conclusion -The collaboration resulting from Novu’s code-first workflow increases agility, enabling rapid iterations and quick adjustments based on user feedback. It fosters effective collaboration between product and engineering teams, resulting in a more efficient workflow and improved user notifications. \ No newline at end of file + +The collaboration resulting from Novu’s code-first workflow increases agility, enabling rapid iterations and quick adjustments based on user feedback. It fosters effective collaboration between product and engineering teams, resulting in a more efficient workflow and improved user notifications. diff --git a/guides/headless-notification-center-guide.mdx b/legacy-guides/headless-notification-center-guide.mdx similarity index 54% rename from guides/headless-notification-center-guide.mdx rename to legacy-guides/headless-notification-center-guide.mdx index 26406e8d..9171a594 100644 --- a/guides/headless-notification-center-guide.mdx +++ b/legacy-guides/headless-notification-center-guide.mdx @@ -1,43 +1,68 @@ --- -title: 'How to use Headless Notification Center' -description: 'Use the headless version of the notification center' +title: "How to use Headless Notification Center" +description: "Use the headless version of the notification center" --- ## Introduction + In this guide, you'll learn about how to use the headless version of our notification center. The headless version is useful for scenarios in which a lightweight solution for integrating notification functionality into a web app that is completely unstyled (so that you can style it as per your wish) is required. -
    + +
    You can find the entire code ( front-end as well as back-end ) of this app [here.](https://github.com/novuhq/novu-headless-demo-app) ## What is the headless notification center -The headless version of Novu's notification library package provides users with a lightweight solution for integrating notification functionality into their web applications. -
    -With just the essential API methods, users can easily incorporate our notification system into any framework or vanilla JavaScript project, without being constrained by our default UI or dependencies. + +The headless version of Novu's notification library package provides users with a lightweight solution for integrating notification functionality into their web applications. + +
    +With just the essential API methods, users can easily incorporate our notification +system into any framework or vanilla JavaScript project, without being constrained +by our default UI or dependencies. This gives the users of the headless notification center greater flexibility and control over what the end-users of a product see and experience. ## How to use the headless notification center + To get started with this you need: -1. A Novu account. [Sign up for free](http://web.novu.co/?utm_campaign=docs-gs-guides-headless) if you don’t have one yet. + +1. A Novu account. [Sign up for free](http://dashboard.novu.co/?utm_campaign=docs-gs-guides-headless) if you don’t have one yet. 2. A working dev environment in the framework of your choice (or vanilla JS). Here, we'll use a React environment ## Workflow set up in Novu + Once, you have signed up for Novu and have a dev environment, follow the steps below: 1. Head over to the Novu Dashboard. 2. Click `Workflows` on the left sidebar of your Novu dashboard. 3. Click the `Create Workflow` button on the top right: - -Once you click the `create workflow` button, you’ll see a dropdown. Select `blank workflow` from the dropdown: - -You’ll now be taken to the workflow editor: - -Once here, you can add the channels you want to use for sending notifications and configure them. For this guide, we’ll use the `In-App` channel. - -The `In-app` node allows you to further customize the notifications that will get sent, as per your need. In our case, it is a simple `description`, which will contain the text that will be entered from the front end. Here's the `in-app` node for your reference: - - + + {" "} + + Once you click the `create workflow` button, you’ll see a dropdown. Select `blank + workflow` from the dropdown: + + {" "} + + You’ll now be taken to the workflow editor: + + {" "} + + Once here, you can add the channels you want to use for sending notifications + and configure them. For this guide, we’ll use the `In-App` channel. + + {" "} + + The `In-app` node allows you to further customize the notifications that will + get sent, as per your need. In our case, it is a simple `description`, which will + contain the text that will be entered from the front end. Here's the `in-app` + node for your reference: + + + {" "} + Here's a brief overview of all the options: + - **1-Preview**: This shows you a glimpse of what each notification item will look like in the Notification Center UI. - **2-Avatar:** If turned on, each notification item will show the avatar of the subscriber. - **3-Action:** With this, you can add a primary and secondary call to action button to each notification item. @@ -46,79 +71,93 @@ Here's a brief overview of all the options: - **6-Filter** - This feature allows you to configure the criteria for delivering notifications. For instance, you can apply a filter based on a subscriber's online status to send them an email if they were online within the last hour. Once you’re done configuring this to your liking, click on the `update` button on the top right. It'll automatically create a trigger code that you can use in your app. To get it, click on the `get snippet` button on the top right and copy it: - -
    + + + {" "} + +
    Let's now see how to add it to our app! ## Pre-requisites: + To be able to use the headless notification center, you'll first need the following: -1. `applicationIdentifier`: You can find this from the `settings` menu in the [Novu web dashboard.](https://web.novu.co/settings?utm_campaign=docs-guides-headless) -2. `subscriberID`: In most real-world apps, you'll get this from a database. It is used to uniquely identify the entity to whom a notification will be delivered. In our case, we're assuming the `subscriberID` to be `12345`. + +1. `applicationIdentifier`: You can find this from the `settings` menu in the [Novu web dashboard.](https://dashboard.novu.co/settings?utm_campaign=docs-guides-headless) +2. `subscriberID`: In most real-world apps, you'll get this from a database. It is used to uniquely identify the entity to whom a notification will be delivered. In our case, we're assuming the `subscriberID` to be `12345`. ## Setting up the back end: + For our demo app, we're setting up the back end as follows: + 1. Install the Novu package. Open your terminal and run the following command: + ```bash npm install @novu/node ``` + Once installed, you can import Novu into your app and initialize it using your Novu account credentials. This step establishes a connection between your app and the Novu notification service. + ```bash import { Novu } from '@novu/node'; const novu = new Novu(''); ``` + -Replace the `` value with the authentic key from the API Key section of your [Novu Dashboard.](http://web.novu.co/?utm_campaign=docs-guides-headless) +Replace the `` value with the authentic key from the API Key section of your [Novu Dashboard.](http://dashboard.novu.co/?utm_campaign=docs-guides-headless) 🔑 Please do not hardcode your credentials in a file in production. Use environment variables instead. Using the above info, we'll write a function that will help us interact with Novu's notification system: + ```javascript -import { Novu } from '@novu/node'; +import { Novu } from "@novu/node"; export const notification = async (description) => { - const novu = new Novu(""); - await novu.subscribers.identify(process.env.SUSBSCRIBER_ID, { - firstName: 'newSubForHeadless', - }); - -// code snippet copied when creating the workflow - await novu.trigger('headless-demo', { - to: { - subscriberId: process.env.SUSBSCRIBER_ID - }, - payload: { - description: description - } - }); - -} + const novu = new Novu(""); + await novu.subscribers.identify(process.env.SUSBSCRIBER_ID, { + firstName: "newSubForHeadless", + }); + + // code snippet copied when creating the workflow + await novu.trigger("headless-demo", { + to: { + subscriberId: process.env.SUSBSCRIBER_ID, + }, + payload: { + description: description, + }, + }); +}; ``` + After this, we'll set up a simple controller function for handling notifications: + ```javascript import { notification } from "../novu/novu.js"; -export const notifController = async (req,res) => { - const{description} = req.body; - try { - await notification(description) - res.status(201).json({message:"Subscriber created successfully"}); - } catch (error) { - console.log("notifController error:",error); - res.status(500).json({message: error.message}) - } -} +export const notifController = async (req, res) => { + const { description } = req.body; + try { + await notification(description); + res.status(201).json({ message: "Subscriber created successfully" }); + } catch (error) { + console.log("notifController error:", error); + res.status(500).json({ message: error.message }); + } +}; ``` And finally, we'll need to establish routes that we'll hit from the front end: + ```javascript import express from "express"; import { notifController } from "../controller/notif.js"; const router = express.Router(); -router.post("/create-sub",notifController); +router.post("/create-sub", notifController); export default router; ``` @@ -126,210 +165,220 @@ export default router; Now, we'll move to the front end because that's where all the magic happens! ## Setting up the front end: + In the front end, install the headless package: + ```bash npm install @novu/headless ``` Then, import it and initialize the headless service: + ```javascript -import { HeadlessService, FetchResult, ISession } from '@novu/headless'; - - useEffect(() => { - - const headlessService = new HeadlessService({ - applicationIdentifier: 'SWMw97ec1ZNA', - subscriberId: '12345', - }); - - headlessService.initializeSession({ - listener: (res) => { - }, - onSuccess: (session) => { - headlessServiceRef.current = headlessService; - fetchNotifications(); - }, - onError: (error) => { - console.log("headlessSice error:", error); - }, - }) - - }, [fetchNotifications]) +import { HeadlessService, FetchResult, ISession } from "@novu/headless"; + +useEffect(() => { + const headlessService = new HeadlessService({ + applicationIdentifier: "SWMw97ec1ZNA", + subscriberId: "12345", + }); + + headlessService.initializeSession({ + listener: (res) => {}, + onSuccess: (session) => { + headlessServiceRef.current = headlessService; + fetchNotifications(); + }, + onError: (error) => { + console.log("headlessSice error:", error); + }, + }); +}, [fetchNotifications]); ``` We've added the `fetchNotifications` function inside a useEffect because we want the effect to run whenever the `fetchNotifications` executes. Here's the `fetchNotifications` function for reference: ```javascript const fetchNotifications = useCallback(() => { - const headlessService = headlessServiceRef.current; - if (headlessService) { - headlessService.fetchNotifications({ - listener: ({ data, error, isError, isFetching, isLoading, status }) => { - // Handle the state of the fetching process and errors here. - }, - onSuccess: (response) => { - - // Handle the fetched notifications here. - setNotifications(response.data); // Store notifications in the state - }, - page: pageNum, // page number to be fetched - }); - } - }, [pageNum]) + const headlessService = headlessServiceRef.current; + if (headlessService) { + headlessService.fetchNotifications({ + listener: ({ data, error, isError, isFetching, isLoading, status }) => { + // Handle the state of the fetching process and errors here. + }, + onSuccess: (response) => { + // Handle the fetched notifications here. + setNotifications(response.data); // Store notifications in the state + }, + page: pageNum, // page number to be fetched + }); + } +}, [pageNum]); ``` Here, we've memoized the `fetchNotifications` to optimize performance and passed `pageNum` in a dependency array so that it reruns when the page number changes. -
    -You can refer to the entire context [here](https://github.com/novuhq/novu-headless-demo-app/blob/main/frontend/src/context/NotificationContext.js) for a better understanding. + +
    +You can refer to the entire context [here](https://github.com/novuhq/novu-headless-demo-app/blob/main/frontend/src/context/NotificationContext.js) +for a better understanding. We've also used several methods related to the Headless Notification Center package. You can find the complete list [here.](/notification-center/client/headless/api-reference) The methods that we've used in our app are `markNotificationsAsRead`, `deleteNotification`, and `markAllMessagesAsRead`. They are as follows: ```javascript - - const markNotificationsAsRead = (messageIds) => { - if (!Array.isArray(messageIds)) { - messageIds = [messageIds]; - } - - const headlessService = headlessServiceRef.current; - - if (headlessService) { - headlessService.markNotificationsAsRead({ - messageId: messageIds, - listener: (result) => { - }, - onError: (error) => { - console.error('Error marking notifications as read:', error); - }, - }); - } - - }; - +const markNotificationsAsRead = (messageIds) => { + if (!Array.isArray(messageIds)) { + messageIds = [messageIds]; + } + + const headlessService = headlessServiceRef.current; + + if (headlessService) { + headlessService.markNotificationsAsRead({ + messageId: messageIds, + listener: (result) => {}, + onError: (error) => { + console.error("Error marking notifications as read:", error); + }, + }); + } +}; const deleteNotification = (messageId) => { - const headlessService = headlessServiceRef.current; - if (headlessService) { - headlessService.removeNotification({ - messageId: messageId, - listener: function (result) { - }, - onSuccess: function (message) { - }, - onError: function (error) { - console.error(error); - }, - messageIds: 'messageOne', - }); - - } - } + const headlessService = headlessServiceRef.current; + if (headlessService) { + headlessService.removeNotification({ + messageId: messageId, + listener: function (result) {}, + onSuccess: function (message) {}, + onError: function (error) { + console.error(error); + }, + messageIds: "messageOne", + }); + } +}; - const markAllMessagesAsRead = (feedId) => { - const headlessService = headlessServiceRef.current; - - headlessService.markAllMessagesAsRead({ - listener: (result) => { - console.log(result); - // Handle the result of marking all messages as read - // You can update the state or perform other actions here - }, - onError: (error) => { - console.error('Error marking all messages as read:', error); - // Implement error handling if needed - }, - feedId: feedId, // Pass the feed ID here, it can be an array or a single ID - }); - }; +const markAllMessagesAsRead = (feedId) => { + const headlessService = headlessServiceRef.current; + + headlessService.markAllMessagesAsRead({ + listener: (result) => { + console.log(result); + // Handle the result of marking all messages as read + // You can update the state or perform other actions here + }, + onError: (error) => { + console.error("Error marking all messages as read:", error); + // Implement error handling if needed + }, + feedId: feedId, // Pass the feed ID here, it can be an array or a single ID + }); +}; ``` -You can view the entire API reference [here](/notification-center/client/headless/api-reference) and pick up the methods that you'd like to use for your specific requirements. + You can view the entire API reference + [here](/notification-center/client/headless/api-reference) and pick up the + methods that you'd like to use for your specific requirements. ### Coding the UI up: + On the UI front, we have a simple form with a submit handler function and a couple of components: + ```javascript const Body = () => { + const [description, setDescription] = useState(""); + const { active, setActive } = useNotification(); + const onSubmitHandler = async (e) => { + e.preventDefault(); + await axios.post( + "https://headless-backend-qx89.onrender.com/api/v1/create-sub", + { description } + ); - const [description, setDescription] = useState(''); - const { active, setActive } = useNotification(); - const onSubmitHandler = async e => { - e.preventDefault() - await axios.post('https://headless-backend-qx89.onrender.com/api/v1/create-sub', { description }) - - setDescription('') - setActive(true) - } - const onChangeHandler = e => { - setDescription(e.target.value); - } - return ( -
    -

    Enter notification text

    - - - - -
    - ) -} + setDescription(""); + setActive(true); + }; + const onChangeHandler = (e) => { + setDescription(e.target.value); + }; + return ( +
    +

    Enter notification text

    +
    + + +
    +
    + ); +}; ``` + The first component is the `appBar` component which includes a bell icon, which can show notifications in a modal when clicked. The modal is displayed when showModal is true, and the modal can be closed by clicking outside of it. The component interacts with the notification-related functionalities provided by the useNotification hook: ```javascript const AppBar = () => { - const [showModal, setShowModal] = useState(false); - const modalRef = useRef(null); - - const { fetchNotifications, setActive, active } = useNotification(); - - const handleShowNotification = async (e) => { - e.stopPropagation(); - setShowModal((prev) => !prev); - await fetchNotifications(); - setActive(false); + const [showModal, setShowModal] = useState(false); + const modalRef = useRef(null); + + const { fetchNotifications, setActive, active } = useNotification(); + + const handleShowNotification = async (e) => { + e.stopPropagation(); + setShowModal((prev) => !prev); + await fetchNotifications(); + setActive(false); + }; + const handleCloseModal = () => { + setShowModal(false); + }; + useEffect(() => { + const handleDocumentClick = (e) => { + if (modalRef.current && !modalRef.current.contains(e.target)) { + handleCloseModal(); + } }; - const handleCloseModal = () => { - setShowModal(false); + if (showModal) { + document.addEventListener("click", handleDocumentClick); } - useEffect(() => { - const handleDocumentClick = e => { - if (modalRef.current && !modalRef.current.contains(e.target)) { - handleCloseModal(); - } - } - if (showModal) { - document.addEventListener('click', handleDocumentClick); - } - return () => { - document.removeEventListener('click', handleDocumentClick) - } - }, [showModal]) - - return ( -
    -
    - - -
    -
    -
    -
    -
    -
    - {showModal && ()} -
    -
    - ); + return () => { + document.removeEventListener("click", handleDocumentClick); + }; + }, [showModal]); + + return ( +
    +
    + + +
    +
    +
    +
    +
    +
    + {showModal && } +
    +
    + ); }; export default AppBar; ``` -And the second component is the `NotificationModal` which is what gets displayed when the user clicks on the bell icon. +And the second component is the `NotificationModal` which is what gets displayed when the user clicks on the bell icon. + The beauty of using the headless version of the notification center is that you can customise exactly how it looks and which functionalities you need in it. @@ -399,7 +448,9 @@ const NotificationModal = () => {
    ) + } + ``` Finally, we've used some css to style the app to our liking. You can see that [here.](https://github.com/novuhq/novu-headless-demo-app/tree/main/frontend/src/styles). @@ -408,4 +459,5 @@ If you've followed everything till here, you'll end up with an app like the foll Again, don't forget that you can view the entire code [here](https://github.com/novuhq/novu-headless-demo-app/tree/main) and if you've any questions, either related to this headless app or something else, feel free to ping use [here.](https://discord.gg/novu) Also, we've several demo apps for you to check out [here.](/demos/introduction) -Thanks for reading! \ No newline at end of file +Thanks for reading! +``` diff --git a/guides/novu-fcm-react-native-android.mdx b/legacy-guides/novu-fcm-react-native-android.mdx similarity index 63% rename from guides/novu-fcm-react-native-android.mdx rename to legacy-guides/novu-fcm-react-native-android.mdx index 9ae21b2e..d9905034 100644 --- a/guides/novu-fcm-react-native-android.mdx +++ b/legacy-guides/novu-fcm-react-native-android.mdx @@ -1,41 +1,47 @@ --- -title: 'How to send push notifications in an Android app (react native) with FCM and Novu' -description: 'Learn how to send FCM push notifications in a React native Android app using Novu and FCM' +title: "How to send push notifications in an Android app (react native) with FCM and Novu" +description: "Learn how to send FCM push notifications in a React native Android app using Novu and FCM" --- ## Prerequisites To complete this tutorial, you will need the following: - - A [Firebase](https://firebase.google.com/) account -- A [Novu](https://web.novu.co/?utm_campaign=docs-gs-guides-fcm-android) account +- A [Novu](https://dashboard.novu.co/?utm_campaign=docs-gs-guides-fcm-android) account - An [Expo](https://expo.dev) account -Push notifications are notifications that are sent to a user’s devices whether the user is using the app or not. We’ll be using the Firebase Cloud Messaging (FCM) integration of Novu to send these notifications. You can find the complete code for this project in our [Github repo.](https://github.com/novuhq/fcm-react-native-android) +Push notifications are notifications that are sent to a user’s devices whether the user is using the app or not. We’ll be using the Firebase Cloud Messaging (FCM) integration of Novu to send these notifications. You can find the complete code for this project in our [Github repo.](https://github.com/novuhq/fcm-react-native-android) ## Start a React-Native project + We'll use the `create-expo` tool to initialize a new expo app. It creates a new React Native app with the expo package installed. We're naming our app `fcm-novu-rn2` and hence using the following command: -``` javascript + +```javascript npx create-expo-app fcm-novu-rn2 ``` + If it asks you to choose a template, choose the 'blank' template. Now, go to the project directory and install the `react-native-firebase` package using the following command: -``` bash +```bash npm install --save @react-native-firebase/app ``` + Once this is installed, install the messaging module of Firebase using the following command: -``` bash + +```bash npm i --save @react-native-firebase/messaging ``` ## Getting the FCM token + Now, we need to grab the `token key` from FCM. We'll need this to send notifications using FCM. We're using `useEffect` to grab this key as follows: -``` jsx + +```jsx // import messaging module -import messaging from '@react-native-firebase/messaging'; +import messaging from "@react-native-firebase/messaging"; // Request permission to send notifications const authStatus = await messaging().requestPermission(); @@ -48,40 +54,41 @@ const enabled = // If permission is granted or provisional, proceed if (enabled) { // Log the authorization status - console.log('Authorization status:', authStatus); + console.log("Authorization status:", authStatus); // Retrieve the FCM token for the device const token = await messaging().getToken(); // Log the FCM token - console.log('FCM Token is:', token); + console.log("FCM Token is:", token); // Return a function to unsubscribe from notifications return unsubscribe; } else { // If permission is not granted, log the error status - console.log('Could not grab token...', authStatus); + console.log("Could not grab token...", authStatus); } - ``` ## FCM handlers + FCM uses something called a `background handler` to, as the name suggests handle notifications when the app is in the background. So, we'll add it to our app: -``` jsx + +```jsx // Background message handler for incoming messages while the app is in bg messaging().setBackgroundMessageHandler(async (remoteMessage) => { // Log a message indicating that a message was handled in the background - console.log('Message handled in the background!', remoteMessage); + console.log("Message handled in the background!", remoteMessage); }); - ``` Similarly, we also need a `foreground handler` to handle notifications when the app is in the foreground. In our case, we're updating a state's value in our handler. Here's our foreground handler: -``` jsx + +```jsx // handle incoming messages while the app is in the foreground const unsubscribe = messaging().onMessage(async (remoteMessage) => { // Log a message indicating that the onMessage event is working - console.log('working!!!!'); - + console.log("working!!!!"); + // Update state with the received message setNotification(remoteMessage); }); @@ -89,40 +96,64 @@ return unsubscribe; ``` ## Setting up Expo + In order to load projects from Expo CLI, we need a dev client to be installed in our app. To install it, execute the following command from the root of the project directory: -``` bash + +```bash npx expo install expo-dev-client ``` Once installed, log into your expo account using the following command: -``` bash +```bash eas login ``` + It'll ask for your username and password and log you in. Now, we are ready to create our build of the app. Use the following command to create a development build of the app for the Android platform: -``` bash + +```bash eas build --profile development --platform android ``` -If expo asks if you want to automatically create an EAS project for your app, choose yes. Do the same when it asks you if you want to generate a new keystore. -Do not change the Android package name, even if EAS asks you to! + + + If expo asks if you want to automatically create an EAS project for your app, + choose yes. Do the same when it asks you if you want to generate a new + keystore. + + + Do not change the Android package name, even if EAS asks you to! + ## Setting up Firebase + 1. Create a Firebase account if you don't already have one. Then, create a new project and all the relevant details. - + + {" "} + 2. Once your project has been created, you'll be greeted with this welcome screen and asked to add Firebase to your app, so let's do that. - + + {" "} + 3. We'll choose the 'Android' option because we're creating an Android app. - + + {" "} + 4. Find the package name from the `app.json` file of the project: - + + {" "} + 5. Add this package name to Firebase: - + + {" "} + 6. Now, we need to add `SHA1` keys from our project to the Firebase app. Run the following command to obtain `SHA1` key. We'll get two `SHA1` keys, one of which we'll add to Firebase right now and the other one, later on. -``` bash + +```bash eas credentials ``` + If it asks you to, choose the 'Android' platform then 'Development' build then 'Keystore' then 'Set up a new keystore' then 'Select Build Credentials' and then 'Create a new build credential configuration'. 7. Now copy the `SHA1` key and add it to Firebase and register the app: @@ -148,48 +179,80 @@ eas build --profile development --platform android 15. If you have an Android device with you, simply scan and install the app. Otherwise, if you want to run it on an emulator, simply copy the link given alongside the QR code and open it on the emulator and then install the app. 16. Start the server to run the app, using the following command: -``` bash + +```bash npx expo start --dev-client ``` + Again, you can either scan the QR code or copy and paste the link. - -Sometimes it doesn't fire up the emulator even when you have it installed. In that case, start the emulator manually and run the server again! + + + {" "} + + + Sometimes it doesn't fire up the emulator even when you have it installed. In + that case, start the emulator manually and run the server again! + ## Setting up Novu + We'll need a workflow to trigger notifications to our user's devices. Follow the steps below to create one to ensure we are on the path to making FCM work as expected: -1. Head over to the [Integrations Store](https://web.novu.co/integrations?utm_campaign=docs-guides-fcm-android) on the Novu Web Dashboard and make sure that you've turned on the 'Firebase Cloud Messaging' integration. - -If you're doing this for the first time, you'll need to get your service account key from the [Firebase Console](https://console.firebase.google.com/). Read more about it in our [FCM provider docs.](https://docs.novu.co/channels-and-providers/push/fcm) -2. Now, we need to create a workflow that we'll trigger from our app to send notifications. Head over to [workflows in the Novu Web Dashboard](https://web.novu.co/workflows?utm_campaign=docs-guides-fcm-android) and click on the 'Add a workflow' button. - + +1. Head over to the [Integrations Store](https://dashboard.novu.co/integrations?utm_campaign=docs-guides-fcm-android) on the Novu Web Dashboard and make sure that you've turned on the 'Firebase Cloud Messaging' integration. + + {" "} + + + If you're doing this for the first time, you'll need to get your service + account key from the [Firebase + Console](https://console.firebase.google.com/). Read more about it in our + [FCM provider docs.](https://docs.novu.co/channels-and-providers/push/fcm) + +2. Now, we need to create a workflow that we'll trigger from our app to send notifications. Head over to [workflows in the Novu Web Dashboard](https://dashboard.novu.co/workflows?utm_campaign=docs-guides-fcm-android) and click on the 'Add a workflow' button. + + {" "} + 3. Choose the 'blank workflow' option from the dropdown menu. - -3. Drag and drop the 'Push' node below the Workflow Trigger node. - -4. If you hover over the newly added 'Push' node, you'll see an error saying 'Message content and title are missing'. - -5. We're going to add a title and body for notifications. So, add them and give your workflow a suitable name. - -6. Now, click on the 'Update' and then the 'get snippet' button to get the trigger code. - + + {" "} + +4. Drag and drop the 'Push' node below the Workflow Trigger node. + + {" "} + +5. If you hover over the newly added 'Push' node, you'll see an error saying 'Message content and title are missing'. + + {" "} + +6. We're going to add a title and body for notifications. So, add them and give your workflow a suitable name. + + {" "} + +7. Now, click on the 'Update' and then the 'get snippet' button to get the trigger code. + + {" "} + ## Create a subscriber and update credentials + A subscriber is essentially the recipient of the notifications sent through Novu’s system. When you create a subscriber, you’re setting up the necessary information for Novu to send targeted notifications to. You can find more about subscribers in [our docs.](https://docs.novu.co/subscribers/subscribers) To create a subscriber, we'll use the `identify` method: -``` jsx + +```jsx // creating new instance of Novu const novu = new Novu(""); // creating subscriber -await novu.subscribers.identify('newSubForRNA', { - firstName: "RNA" +await novu.subscribers.identify("newSubForRNA", { + firstName: "RNA", }); ``` Now, we'll add the FCM token that we'd received [earlier](#getting-the-fcm-token) to this subscriber using the `setCredentials` method: -``` jsx -await novu.subscribers.setCredentials('newSubForRNA', PushProviderIdEnum.FCM, { - deviceTokens: [''], + +```jsx +await novu.subscribers.setCredentials("newSubForRNA", PushProviderIdEnum.FCM, { + deviceTokens: [""], }); // notice the use of 'newSubForRNA' here. This identifies the subscriber to which we're adding the FCM token ``` @@ -197,125 +260,152 @@ await novu.subscribers.setCredentials('newSubForRNA', PushProviderIdEnum.FCM, { ## Sending a push notification using Novu To send a notification with Novu, we are actually triggering a notification workflow. Here's how to do it using Node: -``` jsx -novu.trigger('rna-2', { - to: { - subscriberId: 'newSubForRNA' - }, - payload: {}, -}) + +```jsx +novu.trigger("rna-2", { + to: { + subscriberId: "newSubForRNA", + }, + payload: {}, +}); ``` + When triggered using the trigger code from above, this is the notification received on the device: - + + + {" "} + ## Dynamic content + So far, we've sent notifications that have had "hard-coded" content. Now we'll send dynamic data when triggering a notification. -The way to do so is by changing the values of the title and the body to `{{title}}` and `{{body}}` in our workflow. +The way to do so is by changing the values of the title and the body to `{{title}}` and `{{body}}` in our workflow. 1. Go to the workflow we'd created earlier and change the values of the title and the body to `{{title}}` and `{{body}}` respectively. - + + {" "} + 2. Simply add these identifiers to the payload by changing the trigger code: -``` jsx -novu.trigger('rna-2', { - to: { - subscriberId: 'newSubForRNA' - }, - payload: { - title: payloadTitle, - body: payloadBody - }, + +```jsx +novu.trigger("rna-2", { + to: { + subscriberId: "newSubForRNA", + }, + payload: { + title: payloadTitle, + body: payloadBody, + }, }); ``` -We're extracting `payloadTitle` and `payloadBody` from the request body -Now, the notification delivered to the subscriber will contain dynamic data as shown below: - + + + We're extracting `payloadTitle` and `payloadBody` from the request body + +Now, the notification delivered to the subscriber will contain dynamic data as shown +below: + + {" "} + ## Sending images + Up to this point, your notifications have all contained only text. But if you’ve received many notifications, you know that notifications can have rich content, such as images. It’d be great if your notifications showed users a nice image related to their content. Once again, Firebase makes this super simple. All you need to do is to specify the `URL` of the image in the override as shown below: -``` jsx -novu.trigger('rna-2', { - to: { - subscriberId: 'newSubForRNA' - }, - payload: { - title: payloadTitle, - body: payloadBody + +```jsx +novu.trigger("rna-2", { + to: { + subscriberId: "newSubForRNA", + }, + payload: { + title: payloadTitle, + body: payloadBody, + }, + overrides: { + fcm: { + android: { + notification: { + imageUrl: "URL of the image", + }, + }, }, - overrides: { - fcm: { - android: { - notification: { - imageUrl: 'URL of the image', - } - } - } - } + }, }); ``` + The following notification is what gets delivered to the subscriber: - + + {" "} + ## Handling data type messages + With FCM, you can send `Notification` and `Data` messages. We have handled sending notification messages. Let’s handle data messages. Data type messages are useful when, in addition to the usual data, you want to send some custom data. Maybe whether a subscriber has purchased the pro plan of our product or not, maybe which tier of usage they have purchased etc. You can do this easily by modifying the override. Here's an example of sending the subscription status using the identifier `subStatus`: -``` jsx -novu.trigger('rna-2', { - to: { - subscriberId: 'newSubForRNA' - }, - payload: { - title: payloadTitle, - body: payloadBody +```jsx +novu.trigger("rna-2", { + to: { + subscriberId: "newSubForRNA", + }, + payload: { + title: payloadTitle, + body: payloadBody, + }, + overrides: { + fcm: { + android: { + data: { + subStatus: "true", + }, + }, }, - overrides: { - fcm: { - android: { - data: { - subStatus: "true" - }, - } - } - } + }, }); ``` On the user's device, you can extract this out and have it displayed like this: - + + + {" "} + ## Priority of a notification FCM tries to deliver high-priority messages immediately. The priority of a notification can also be altered by changing the overrides, as shown below: -``` jsx -novu.trigger('rna-2', { - to: { - subscriberId: 'newSubForRNA' - }, - payload: { - title: payloadTitle, - body: payloadBody +```jsx +novu.trigger("rna-2", { + to: { + subscriberId: "newSubForRNA", + }, + payload: { + title: payloadTitle, + body: payloadBody, + }, + overrides: { + fcm: { + android: { + data: { + subStatus: "true", + }, + notification: { + priority: "low", + }, + }, }, - overrides: { - fcm: { - android: { - data: { - subStatus: "true" - }, - notification: { - priority: 'low' - } - } - } - } + }, }); ``` - + + {" "} + ## Additional resources + - [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging) -- [FCM Architectural Overview](https://firebase.google.com/docs/cloud-messaging/fcm-architecture) \ No newline at end of file +- [FCM Architectural Overview](https://firebase.google.com/docs/cloud-messaging/fcm-architecture) diff --git a/legacy-guides/novu-fcm-web.mdx b/legacy-guides/novu-fcm-web.mdx new file mode 100644 index 00000000..dc88c928 --- /dev/null +++ b/legacy-guides/novu-fcm-web.mdx @@ -0,0 +1,275 @@ +--- +title: "How to send web push notifications with FCM and Novu" +description: "Send FCM push notifications in a React app using Novu" +--- + +## Introduction + +In this guide, we'll learn how to send FCM push notifications in a web app using Novu. But before exploring the actual code, let’s understand what a push notification is and how it works. +You can find the [frontend code](https://github.com/novuhq/fcmWebPushFE) as well as [backend code](https://github.com/novuhq/FCMWebPushBE) for the app on Github. + +
    +Push notifications are notifications that are sent to a user's devices whether the +user is using the app or not. We'll be using Firebase Cloud Messaging (FCM) integration +of Novu to send these notifications. This guide will be broken down into three parts: +1. Setting up Novu. 2. Setting up Firebase. 3. Adding Firebase to the Frontend. 4. +Adding Novu to the Backend. + +## Setting up Firebase + +1. Create a Firebase account if you don't already have one. Then, create a new project and all the relevant details. + + {" "} + +2. Once your project has been created, you'll be greeted with this welcome screen and asked to add Firebase to your app, so let's do that. + + {" "} + +3. We'll choose the web option because we're creating a web app. + + {" "} + +4. Give your app a name and click on the 'Register' button. + + {" "} + +5. We're done with the Firebase setup! Now, we'll set up Novu and finally proceed to add both (Novu and FCM) to our app. + +## Setting up Novu + +We'll need a workflow to trigger notifications to our user's devices. Follow the steps below to create one to ensure we are on the path to make FCM work as expected: + +1. Head over to the [Integrations Store](https://dashboard.novu.co/integrations?utm_campaign=docs-guides-fcm-web) on the Novu Web Dashboard and make sure that you've turned on the 'Firebase Cloud Messaging' integration. + + {" "} + + + If you're doing this for the first time, you'll need to get your service + account key from the [Firebase + Console](https://console.firebase.google.com/). Read more about it in our + [FCM provider docs.](https://docs.novu.co/channels-and-providers/push/fcm) + +2. Now, we need to create a workflow that we'll trigger from our app to send notifications. Head over to [workflows in the Novu Web Dashboard](https://dashboard.novu.co/workflows?utm_campaign=docs-guides-fcm-web) and click on the 'Add a workflow' button. + + {" "} + +3. Choose the 'blank workflow' option from the dropdown menu. + + {" "} + +4. Drag and drop the 'Push' node below the Workflow Trigger node. + + {" "} + +5. If you hover over the newly added 'Push' node, you'll see an error saying 'Message content and title are missing'. + + {" "} + +6. We're going to use the identifiers `title` and `body` for the title and the content of notifications, so let's add them in the 'Push message title' and the 'Push message content' fields respectively. So, add them and give your workflow a suitable name. + + {" "} + +7. Now, click on the 'get snippet' button to get the trigger code. + + {" "} + + +## Adding Firebase to the frontend + +1. In your project, add Firebase using the following command: + +```bash +npm install firebase +``` + +2. Create a new file called `firebase.js` and add the following to it: + +```jsx +import { initializeApp } from "firebase/app"; +import { getMessaging, onMessage } from "firebase/messaging"; + +//from firebase console +// Your web app's Firebase configuration +const firebaseConfig = { + apiKey: import.meta.env.VITE_FIREBASE_API_KEY, + authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN, + projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID, + storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET, + messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID, + appId: import.meta.env.VITE_FIREBASE_APP_ID, +}; + +// Initialize Firebase +export const app = initializeApp(firebaseConfig); + +// Initialize Firebase Cloud Messaging and get a reference to the service +export const messaging = getMessaging(app); + +export const onMessageListener = () => + new Promise((resolve) => { + onMessage(messaging, (payload) => { + console.log("payload", payload); + resolve(payload); + }); + }); +``` + +3. Now, we need to generate tokens using the `getToken` method provided by Firebase. But we want to use this only when the user has allowed notifications on their end. So let's request notification permission from the user and if granted, we'll generate the token. Add this to your root module: + +```jsx +async function requestPermission() { + const permission = await Notification.requestPermission(); + if (permission === "granted") { + // get token + const token = await getToken(messaging, { + vapidkey: import.meta.env.VITE_VAPID_KEY, + }); + } else if (permission === "denied") { + alert("Persmission denied!"); + } +} +useEffect(() => { + requestPermission(); +}, []); +``` + +4. To use the `getToken` method, you need a Voluntary Application Server Identification or `VAPID key`. Go to your Project Settings -> Cloud Messaging -> Generate key pair (under Web Push Certificates): + + {" "} + +5. In order for Firebase's background listener service to work, you need a service worker. Make sure you've created a service worker file `firebase-messaging-sw.js` and add the following to it: + +```jsx +importScripts("https://www.gstatic.com/firebasejs/8.10.0/firebase-app.js"); +importScripts( + "https://www.gstatic.com/firebasejs/8.10.0/firebase-messaging.js" +); + +const firebaseConfig = { + apiKey: import.meta.env.VITE_FIREBASE_API_KEY, + authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN, + projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID, + storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET, + messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID, + appId: import.meta.env.VITE_FIREBASE_APP_ID, +}; + +firebase.initializeApp(firebaseConfig); +const messaging = firebase.messaging(); +messaging.onBackgroundMessage((payload) => { + console.log("notif incoming", payload); + const notificationTitle = JSON.parse(JSON.parse(payload.notification)).title; + // payload.notification.title; + const notificationOptions = { + body: payload.notification.body, + icon: payload.notification.image, + }; + + self.registration.showNotification(notificationTitle, notificationOptions); +}); +``` + +## Adding Novu to the backend + +In your app's backend, add Novu using the following command: + +```bash +npm install @novu/node +``` + +Now, create a route that you want to hit when called from the front end. In our app, this is the route: + +```jsx +import express from "express"; +import { createNotif } from "../controller/notif.js"; + +const router = express.Router(); + +router.post("/create", createNotif); + +export default router; +``` + +Now, we need a controller function to handle the logic for what is to be sent in the trigger function’s payload. In our case, this is the controller function: + +```jsx +import { inAppNotification } from "../novu/novu.js"; + +export const createNotif = async (req, res) => { + const { title, body } = req.body; + try { + await inAppNotification(title, body); + res.status(201).json({ message: "success", title: title, body: body }); + } catch (error) { + res.status(409).json({ message: error.message }); + } +}; +``` + +To make it modular, we’ll keep the trigger code in a separate function in a separate file (`novu.js`, in our case) and the trigger function is getting called in the controller function above by the name `createNotif`. + +If you’re following the guide, you should already have the trigger function. But before we can add it to our app, we need one key thing - **Subscribers.** + +[Subscribers](https://docs.novu.co/subscribers/subscribers) are entities to which the notifications are sent. You can see a list of subscribers in the [Novu dashboard](https://dashboard.novu.co/subscribers?utm_campaign=docs-guides-fcm-web) as well. + +We'll create a subscriber in our app. Our backend will be written in Node.js, but we also have backend [SDKs in PHP, .NET, Go, Ruby, Python, and Kotlin](/sdks/introduction). The recommended way to create a subscriber in NodeJS is as follows: + +```jsx +await novu.subscribers.identify(process.env.NOVU_SUB_ID, { + firstName: "pushSubscriber", +}); +``` + +Here, we're creating a subscriber with the `subscriberID` of whatever value the `env` file contains for the identifier `NOVU_SUB_ID`. You can read more about subscribers [in our docs.](/subscribers/subscribers). + +Back in our app, before we can now add the trigger function, we need to set device identifiers using the [setCredential method](https://docs.novu.co/channels-and-providers/push/fcm#set-device-token): + +```jsx +await novu.subscribers.setCredentials( + process.env.NOVU_SUB_ID, + PushProviderIdEnum.FCM, + { + deviceTokens: [process.env.DEVICE_TOKEN], + } +); +``` + + + You'll need to pass the `subscriberID` and the `deviceTokens` in the + `setCredentials` method. + + +Finally, we can add the trigger code: + +```jsx +import { Novu, PushProviderIdEnum } from "@novu/node"; + +export const inAppNotification = async (title, body) => { + const novu = new Novu(process.env.NOVU_API_KEY); + await novu.subscribers.identify(process.env.NOVU_SUB_ID, { + firstName: "pushSubscriber", + }); + + await novu.subscribers.setCredentials( + process.env.NOVU_SUB_ID, + PushProviderIdEnum.FCM, + { + deviceTokens: [process.env.DEVICE_TOKEN], + } + ); + + novu.trigger(process.env.NOVU_WORKFLOW_ID, { + to: { + subscriberId: process.env.NOVU_SUB_ID, + }, + payload: { + title: title, + body: body, + }, + }); +}; +``` + +We're done with the app. You can find the [frontend code](https://github.com/novuhq/fcmWebPushFE) as well as [backend code](https://github.com/novuhq/FCMWebPushBE) for the app on Github. + +Congratulations on following the guide up until this point. If you’ve done everything as recommended, you’ll end up with an app that uses Firebase Cloud Messaging to send notifications to your users using Novu. diff --git a/legacy-guides/slack-guide.mdx b/legacy-guides/slack-guide.mdx new file mode 100644 index 00000000..c1da4761 --- /dev/null +++ b/legacy-guides/slack-guide.mdx @@ -0,0 +1,266 @@ +--- +title: "How to use Novu to send notifications to a Slack channel" +description: "Learn to send Slack notifications swiftly with Novu (manually managed)" +--- + +# Introduction + +In this guide, you'll learn how to use Novu to send notifications directly to a Slack channel. But before coding anything up, we first need to go through a setup process. +The corresponding docs for this guide are available on our [docs](https://docs.novu.co/channels-and-providers/chat/slack). + + + The entire code of this app (frontend as well as backend) can be found + [here](https://github.com/novuhq/slack-app). + + +So let's begin! + +# Create a Slack App + +Creating a Slack app is fairly simple. Follow these steps to create your app: + +1. Go to [Slack's app dashboard](https://api.slack.com/apps) and click on the 'Create new App' button, as shown in the image: + + {" "} + +2. Choose 'From Scratch' from the following dialog: + + {" "} + +3. Choose a name for your app and select the Slack workspace in which you want to send notifications: + + {" "} + +4. Once you're done, simply click the 'Create App' button: + + {" "} + +5. Once done, you'll be greeted with the screen shown below. We'll make a couple of changes here and it'll be ready to go. + + {" "} + +6. We'll need `Client Id` from the [Slack Developer's Dashboard](https://api.slack.com/apps) for configuring Slack Integration in the [Novu Web Dashboard](https://dashboard.novu.coovu.coovu.co/integrations?utm_campaign=docs-guides-slack) later, so keep it handy. + + {" "} + + +# Create a workflow in the Novu Web Dashboard + +1. To create a workflow, head to the workflow section in the [Novu Web Dashboard](https://dashboard.novu.co/workflows?utm_campaign=docs-guides-slack). +2. Click on the 'Add a workflow' button and select 'Blank workflow' from the dropdown. + + {" "} + +3. Once there, give your workflow a name and drag and drop the 'chat' option below the 'workflow trigger' step. + + {" "} + +4. You can also add variables in the Workflow Editor. For example, here I've added 'chatMsg' as a variable as I'll be sending data using it. + + {" "} + + Whatever is placed inside double braces is a variable. +5. Make sure that you've turned on the Slack integration in the [Integrations Store](https://dashboard.novu.co/integrations?utm_campaign=docs-guides-slack). + + {" "} + +6. To turn the Slack integration on, you'll need `Client Id`. You should have it already but if you don't, you can obtain it from the [Slack Developer's Dashboard](https://api.slack.com/apps). + + {" "} + +7. Once you have it, you need to plug it into the respective field in the [Slack Integration Settings](https://dashboard.novu.co/integrations?utm_campaign=docs-guides-slack) on the Novu Web Dashboard. + + {" "} + + +# Create the backend + +The backend for this app is quite simple. Simply install the Novu package: + +```bash +npm install @novu/node + +``` + +Now, create a route that you'll hit when called from the front end. We'll also need to add it to our Slack app (discussed below). For our demo app, this is the route I've created: + +```jsx +import express from "express"; +import { chatController } from "../controller/chat.js"; + +const router = express.Router(); + +router.post("/sendChat", chatController); + +export default router; +``` + +Now, we need a controller function to handle what is to be sent in the trigger's function payload. Here's the controller I wrote: + +```jsx +import { chat } from "../novu/novu.js"; + +export const chatController = async (req, res) => { + const { chatMsg } = req.body; + try { + await chat(chatMsg); + res.status(201).json({ message: "Message sent successfully" }); + } catch (error) { + res.status(500).json({ message: error.message }); + } +}; +``` + + + Notice how we're expecting 'chatMsg' in our payload. This is why we added it + as a variable in the workflow created earlier. + + +To make it modular, we’ll keep the trigger code in a separate function in a separate file, `novu.js`, in our case, which is as follows: + +```jsx +import { Novu, ChatProviderIdEnum } from "@novu/node"; + +export const chat = async (chatMsg) => { + const novu = new Novu(process.env.YOUR_NOVU_API_KEY_HERE); + await novu.subscribers.identify(process.env.SUB_ID, { + firstName: "newSubForSlackChat", + }); + + await novu.trigger("slack", { + to: { + subscriberId: process.env.SUB_ID, + }, + payload: { + chatMsg: chatMsg, + }, + }); +}; +``` + +In this code snippet above, we're first initializing a new instance of Novu, then using it to 'identify' or [create](https://docs.novu.co/subscribers/subscribers#create-a-subscriber) a [subscriber](https://docs.novu.co/subscribers/subscribers). + + + The 'identify' method tries to find a subscriber with the given info. If it + can't find any such subscriber, it creates a new subscriber with the supplied + info. + +After creating the subscriber, it runs the trigger code for the workflow we had created. +You can find the trigger workflow by clicking on the 'Get Snippet' button on the +workflow: + + {" "} + + + {" "} + + +# Configure the Slack app + +There are two ways to configure the Slack app. One is the Novu managed option and another the manually managed option. +Since the Novu managed is fairly straightforward, I'll demonstrate the manual method here. Follow along to set it up! + +1. Goto 'Incoming Webhooks' in your Slack app settings and turn it on. + + {" "} + +2. Click on the 'Add New Webhook to Workspace': + + {" "} + +3. Now, go ahead and select the channel in which you want to send notifications and click 'allow'. + + {" "} + +4. Then, copy the 'webhookUrl' from Slack. + + {" "} + +5. Now, add the code below to the backend trigger functionality. Using this code, we are specifying what `webhookUrl` Novu will use to authenticate when using the Slack provider. + + + +```javaScript +import { + Novu, + ChatProviderIdEnum +} from '@novu/node'; + +const novu = new Novu(""); + +await novu.subscribers.setCredentials('subscriberId', ChatProviderIdEnum.Slack, { +webhookUrl: "", +}); + +```` + + +```bash +curl -L -X PUT 'https://api.novu.co/v1/subscribers//credentials' \ +-H 'Content-Type: application/json' \ +-H 'Accept: application/json' \ +-H 'Authorization: ApiKey ' \ +-d '{ + "providerId": "slack", + "credentials": { + "webhookUrl": "" + }, + "integrationIdentifier": "slack-MnGLxp8uy" +}' +```` + + + + +In our case, we'll add it to the `Novu.js` file we created earlier so that the final file becomes : + +```jsx +import { Novu, ChatProviderIdEnum } from "@novu/node"; + +export const chat = async (chatMsg) => { + const novu = new Novu(process.env.YOUR_NOVU_API_KEY_HERE); + await novu.subscribers.identify(process.env.SUB_ID, { + firstName: "newSubForSlackChat", + }); + + await novu.subscribers.setCredentials( + process.env.SUB_ID, + ChatProviderIdEnum.Slack, + { + webhookUrl: process.env.SLACK_WEBHOOK_URL, + } + ); + + await novu.trigger("slack", { + to: { + subscriberId: process.env.SUB_ID, + }, + payload: { + chatMsg: chatMsg, + }, + }); +}; +``` + +6. Finally, we'll add the route we'd created earlier to the redirect URL section, as shown: + + {" "} + + + For demonstration purposes, I've added both the local as well as the + deployed URL. You only need one depending on whether you're running this + locally or have a deployed version. + + Now, our backend is all done, our Slack is all setup and all we need to do is + hit the URL + +# Front end set up + +For our demonstration purposes, the front end is pretty basic. All we have to do is have an input field to take the notification text in and send the payload. The front end code for this demo app is available on our [Github repo](https://github.com/novuhq/slack-app/tree/main/frontend). +And here's our app in all its glory! + + + {" "} + + +This is how we send Slack notifications using Novu! diff --git a/guides/usecases/centralized-multiple-legacy-implementations.mdx b/legacy-guides/usecases/centralized-multiple-legacy-implementations.mdx similarity index 94% rename from guides/usecases/centralized-multiple-legacy-implementations.mdx rename to legacy-guides/usecases/centralized-multiple-legacy-implementations.mdx index 6142cc2c..2022e6a0 100644 --- a/guides/usecases/centralized-multiple-legacy-implementations.mdx +++ b/legacy-guides/usecases/centralized-multiple-legacy-implementations.mdx @@ -8,6 +8,6 @@ If you're coming from a medium or large team with multiple implementations of se - Install Novu's SDK (according to your preferred tech). - Connect your existing delivery providers to Novu. - [Migrate your users to subscribers](/subscribers/subscribers#import-subscribers) on Novu either via Cloud or on-prem. -- Now, every notification fires via Novu. +- Now, every notification fires via Novu. Teams in charge of content can always update the workflow templates via the dashboard. Engineers can always debug and add as many providers or activate as many more channels needed! diff --git a/guides/usecases/debug-notifications.mdx b/legacy-guides/usecases/debug-notifications.mdx similarity index 59% rename from guides/usecases/debug-notifications.mdx rename to legacy-guides/usecases/debug-notifications.mdx index 11739103..88171549 100644 --- a/guides/usecases/debug-notifications.mdx +++ b/legacy-guides/usecases/debug-notifications.mdx @@ -5,18 +5,18 @@ description: "Understand the lifecycle of your notifications" Debugging notifications can be a hassle. Novu allows you understand and debug notifications sent across different channels in one place. -In [Novu's Activity Feed](https://web.novu.co/activities?utm_campaign=docs-usecases-debug), you can access a log of all the notifications that have been sent & filter through the successful & failed ones. Furthermore, you'll know why it failed! +In [Novu's Activity Feed](https://dashboard.novu.co/activities?utm_campaign=docs-usecases-debug), you can access a log of all the notifications that have been sent & filter through the successful & failed ones. Furthermore, you'll know why it failed! You can filter through channels, workflows, emails, subscribers and transactions. - +
    - + [Explore more about Novu’s Activity Feed](/activity-feed/introduction) diff --git a/guides/usecases/digest-multiple-events.mdx b/legacy-guides/usecases/digest-multiple-events.mdx similarity index 83% rename from guides/usecases/digest-multiple-events.mdx rename to legacy-guides/usecases/digest-multiple-events.mdx index 410c31d0..352f41a2 100644 --- a/guides/usecases/digest-multiple-events.mdx +++ b/legacy-guides/usecases/digest-multiple-events.mdx @@ -7,9 +7,8 @@ One of the recurring features developers yearn for is the ability to batch multi Novu ships with a built-in digest engine that handles batching and digesting multiple events in a single notification. You can programmatically adjust the digest recurrence or set it from the dashboard. - - + -[Explore more about Novu's Digest Feature](/workflows/digest) \ No newline at end of file +[Explore more about Novu's Digest Feature](/workflows/digest) diff --git a/guides/usecases/embedded-notification-center.mdx b/legacy-guides/usecases/embedded-notification-center.mdx similarity index 89% rename from guides/usecases/embedded-notification-center.mdx rename to legacy-guides/usecases/embedded-notification-center.mdx index 2b0f1d72..4391fa6d 100644 --- a/guides/usecases/embedded-notification-center.mdx +++ b/legacy-guides/usecases/embedded-notification-center.mdx @@ -5,13 +5,12 @@ description: "A real-time fully functional embedded notification center" Novu provides a real-time embedded notification center widget that can simply be dropped into any web application. - - + It's available as a drop-in component for React, Vue and Angular apps. For non-specific frameworks, it's also ready as a bundled web component. We care about the developer experience so much that we also built a headless, zero-UI version to empower you to hand-craft your notification center. -[Explore a lot more about the Notification Center here](/notification-center/introduction) \ No newline at end of file +[Explore a lot more about the Notification Center here](/notification-center/introduction) diff --git a/legacy-guides/usecases/introduction.mdx b/legacy-guides/usecases/introduction.mdx new file mode 100644 index 00000000..dc24e262 --- /dev/null +++ b/legacy-guides/usecases/introduction.mdx @@ -0,0 +1,55 @@ +--- +title: "Learn by usecase" +description: "Learn Novu's Use Cases" +--- + +Novu is built to empower engineering teams in integrating rich product notification experiences seamlessly without re-inventing the wheel. + +It has been thoroughly and beautifully crafted to help individuals, teams, businesses and organizations focus on what truly matters: creating exceptional user experiences and driving engagement through smooth and swift notifications delivery. + +The common use cases for using Novu are highlighted below: + + + + + + + + + + diff --git a/guides/usecases/manage-notifications-in-multiple-languages.mdx b/legacy-guides/usecases/manage-notifications-in-multiple-languages.mdx similarity index 100% rename from guides/usecases/manage-notifications-in-multiple-languages.mdx rename to legacy-guides/usecases/manage-notifications-in-multiple-languages.mdx diff --git a/legacy-guides/usecases/multiple-delivery-providers.mdx b/legacy-guides/usecases/multiple-delivery-providers.mdx new file mode 100644 index 00000000..21d617ec --- /dev/null +++ b/legacy-guides/usecases/multiple-delivery-providers.mdx @@ -0,0 +1,20 @@ +--- +title: "Managing multiple delivery providers in one place" +description: "Learn to manage many delivery providers across different channels in one dashboard" +--- + +[Novu's dashboard](https://dashboard.novu.co/integrations?utm_campaign=docs-usecases-multipledelivery) empowers you to manage all your notification delivery providers across Email, Sms, Push and Chat in one place. + +You can activate, deactivate, modify settings, set primary and secondary active providers as you wish from the dashboard. + +### Integrations Store + + + + + +### Add a new Delivery Provider + + + + diff --git a/guides/usecases/send-notifications-at-any-scale.mdx b/legacy-guides/usecases/send-notifications-at-any-scale.mdx similarity index 63% rename from guides/usecases/send-notifications-at-any-scale.mdx rename to legacy-guides/usecases/send-notifications-at-any-scale.mdx index d22c2e8d..40671c23 100644 --- a/guides/usecases/send-notifications-at-any-scale.mdx +++ b/legacy-guides/usecases/send-notifications-at-any-scale.mdx @@ -3,8 +3,6 @@ title: "Processing and sending notifications at any scale" description: "Novu can handle notifications at extremely large scale" --- -[Novu's Cloud](https://web.novu.co?utm_campaign=docs-usecases-notificationsscale) is built to process and send notifications at any scale. For folks using the self-hosted Novu version, you can scale it up as your needs grow. +[Novu's Cloud](https://dashboard.novu.co?utm_campaign=docs-usecases-notificationsscale) is built to process and send notifications at any scale. For folks using the self-hosted Novu version, you can scale it up as your needs grow. If you're looking to use Novu on a very large scale & you need to confirm infrastructure & scalability prowess, [please reach out to us for more information](https://notify.novu.co/meetings/novuhq/notifications-45min?utm_campaign=docs-bam-usecases-notificationsscale). - - diff --git a/mint.json b/mint.json index 7c0b2578..d492484f 100644 --- a/mint.json +++ b/mint.json @@ -24,33 +24,26 @@ "topbarLinks": [ { "name": "Get API Key", - "url": "https://web.novu.co/settings?utm_campaign=docs-menu-apikey" + "url": "https://dashboard.novu.co/api-keys/Development?utm_campaign=docs-menu-apikey" } ], "topbarCtaButton": { "type": "github", "url": "https://github.com/novuhq/novu" }, - "anchors": [ + "anchors": [], + "tabs": [ { "name": "API Reference", - "icon": "square-terminal", "url": "api-reference" }, { - "name": "Discord Support", - "icon": "discord", - "url": "https://discord.gg/novu?ref=docs-instant-help" - } - ], - "tabs": [ - { - "name": "Framework (Code-First)", - "url": "framework" + "name": "Integrations", + "url": "integrations" }, { - "name": "API Reference", - "url": "api-reference" + "name": "Inbox", + "url": "inbox" }, { "name": "SDKs", @@ -61,309 +54,102 @@ "url": "guides" }, { - "name": "Help Center", - "url": "help" + "name": "Recipes", + "url": "recipes" + }, + { + "name": "Community", + "url": "community" } ], "navigation": [ { - "group": "Introduction", + "group": "Getting Started", "pages": [ "getting-started/introduction", "getting-started/how-novu-works", - "getting-started/concepts" + "getting-started/non-technical" ] }, { - "group": "Architecture", - "pages": [ - "architecture/introduction", - "architecture/diagrams" - ] - }, - { - "group": "Getting Started", - "pages": [ - "getting-started/novu-sign-up", - "getting-started/send-your-first-notification", - { - "group": "Quickstarts", - "icon": "bolt", - "pages": [ - "quickstarts/overview", - "quickstarts/general", - { - "group": "Client-Side", - "pages": [ - "quickstarts/vue", - "quickstarts/react", - "quickstarts/angular", - "quickstarts/vanillajs", - "quickstarts/redwoodjs", - "quickstarts/iFrame", - "quickstarts/nextjs" - ] - }, - { - "group": "Server-Side", - "pages": [ - "quickstarts/nodejs", - "quickstarts/php", - "quickstarts/python", - "quickstarts/kotlin", - "quickstarts/ruby", - "quickstarts/.NET", - "quickstarts/nextjs", - "quickstarts/go", - "quickstarts/java" - ] - } - ] - } - ] - }, - { - "group": "Subscribers", + "group": "Quickstart", "pages": [ - "subscribers/subscribers", - "subscribers/topics", - "subscribers/preferences" + "quickstart/overview", + "quickstart/nextjs", + "quickstart/svelte", + "quickstart/remix", + "quickstart/express", + "quickstart/nuxt", + "quickstart/h3" ] }, { - "group": "Workflows", - "pages": [ - "workflows/notification-workflows", - "workflows/variants", - "workflows/step-filters", - "workflows/digest", - "workflows/delay-action", - "workflows/template-store", - "workflows/messages" - ] - }, - { - "group": "Activity Feed", - "pages": [ - "activity-feed/introduction", - "activity-feed/trigger-event-lifecycle" - ] - }, - { - "group": "Tenants", - "pages": [ - "tenants/introduction" - ] - }, - { - "group": "Platform", - "pages": [ - "platform/environments", - "platform/webhooks-for-providers", - "platform/inbound-parse-webhook" - ] - }, - { - "group": "Notification Center", - "pages": [ - "notification-center/introduction", - { - "group": "UI Library Components", - "icon": "object-group", - "pages": [ - { - "group": "React", - "pages": [ - "notification-center/client/react/get-started", - "notification-center/client/react/customization", - "notification-center/client/react/preference", - "notification-center/client/react/multiple-tabs", - "notification-center/client/react/api-reference" - ] - }, - "notification-center/client/vue", - "notification-center/client/angular", - "notification-center/client/web-component", - "notification-center/client/iframe", - { - "group": "Headless", - "pages": [ - "notification-center/client/headless/get-started", - "notification-center/client/headless/api-reference" - ] - } - ] - }, - "notification-center/backend/methods" - ] - }, - { - "group": "Content Creation & Design", + "group": "Concepts", "pages": [ - "content-creation-design/variables", - "content-creation-design/handlebars-helpers", - "content-creation-design/brand", - "content-creation-design/notification-content-creation", - "content-creation-design/layouts", - "content-creation-design/translations" + "concepts/workflows", + "concepts/controls", + "concepts/trigger", + "concepts/subscribers", + "concepts/endpoint", + "concepts/environments", + "concepts/preferences", + "concepts/tenants", + "concepts/topics" ] }, { - "group": "Channels & Providers", - "pages": [ - "channels-and-providers/introduction", - "channels-and-providers/integration-store", - "channels-and-providers/default-providers", - { - "group": "In-App", - "icon": "bell", - "pages": [ - "channels-and-providers/in-app/introduction" - ] - }, - { - "group": "Email", - "icon": "envelope", - "pages": [ - "channels-and-providers/email/overview", - "channels-and-providers/email/infobip", - "channels-and-providers/email/amazon-ses", - "channels-and-providers/email/braze", - "channels-and-providers/email/mailersend", - "channels-and-providers/email/mailgun", - "channels-and-providers/email/mailjet", - "channels-and-providers/email/mailtrap", - "channels-and-providers/email/mandrill", - "channels-and-providers/email/netcore", - "channels-and-providers/email/outlook365", - "channels-and-providers/email/postmark", - "channels-and-providers/email/resend", - "channels-and-providers/email/sendgrid", - "channels-and-providers/email/sendinblue", - "channels-and-providers/email/sparkpost", - "channels-and-providers/email/plunk", - "channels-and-providers/email/custom-smtp" - ] - }, - { - "group": "SMS", - "icon": "message-sms", - "pages": [ - "channels-and-providers/sms/overview", - "channels-and-providers/sms/twilio", - "channels-and-providers/sms/sms77", - "channels-and-providers/sms/africas-talking", - "channels-and-providers/sms/azure", - "channels-and-providers/sms/bulk-sms", - "channels-and-providers/sms/messagebird", - "channels-and-providers/sms/simpletexting", - "channels-and-providers/sms/infobip", - "channels-and-providers/sms/sendchamp", - "channels-and-providers/sms/aws-sns", - "channels-and-providers/sms/nexmo", - "channels-and-providers/sms/plivo", - "channels-and-providers/sms/telnyx", - "channels-and-providers/sms/termii", - "channels-and-providers/sms/firetext", - "channels-and-providers/sms/gupshup", - "channels-and-providers/sms/clickatell" - ] - }, - { - "group": "Push", - "icon": "mobile", - "pages": [ - "channels-and-providers/push/overview", - "channels-and-providers/push/fcm", - "channels-and-providers/push/apns", - "channels-and-providers/push/expo-push", - "channels-and-providers/push/onesignal", - "channels-and-providers/push/pushpad", - "channels-and-providers/push/push-webhook", - "channels-and-providers/push/pusher-beams" - ] - }, - { - "group": "Chat", - "icon": "comments", - "pages": [ - "channels-and-providers/chat/overview", - "channels-and-providers/chat/discord", - "channels-and-providers/chat/slack", - "channels-and-providers/chat/ms-teams", - "channels-and-providers/chat/zulip", - "channels-and-providers/chat/whats-app" - ] - } - ] + "group": "Architecture", + "pages": ["architecture/introduction", "architecture/diagrams"] }, { - "group": "Self-hosting Novu", + "group": "Building Workflows", "pages": [ - "self-hosting-novu/introduction", - "self-hosting-novu/deploy-with-docker", - "self-hosting-novu/object-storage", - "self-hosting-novu/kubernetes", - "self-hosting-novu/aws" + "workflow/introduction", + "workflow/studio", + "workflow/content", + "workflow/digest", + "workflow/delay", + "workflow/custom" ] }, { - "group": "Guides", - "pages": [ - "guides/add-digest-to-inapp-notifications", - "guides/add-digest-to-email-notifications", - "guides/headless-notification-center-guide", - "guides/slack-guide", - "guides/discord-guide", - "guides/novu-fcm-web", - "guides/FCM-iOS-Novu/how-to-send-PUSH-notifications-to-iOS-devices-with-FCM-using-Novu", - "guides/fcm-flutter-novu/how-to-send-push-notifications-to-flutter-apps-with-fcm-using-Novu", - "guides/how-to-integrate-segment-with-novu", - "guides/novu-fcm-react-native-android", - "guides/framework-guides/framework-react-email", - "guides/framework-guides/framework-nuxt-vuemail", - "guides/framework-guides/framework-svelte", - "guides/framework-guides/framework-remix", - "guides/framework-guides/framework-mjml", - "guides/framework-guides/otp", - "guides/framework-guides/product", - "guides/framework-guides/digest" - ] + "group": "Inbox Component", + "pages": ["inbox/introduction"] }, { - "group": "Recipes", + "group": "React", + "icon": "react", "pages": [ - "guides/cookbook/introduction" + "inbox/react/get-started", + "inbox/react/customization", + "inbox/react/preference", + "inbox/react/multiple-tabs", + "inbox/react/api-reference" ] }, { - "group": "Usecases", - "pages": [ - "guides/usecases/introduction", - "guides/usecases/multiple-delivery-providers", - "guides/usecases/embedded-notification-center", - "guides/usecases/digest-multiple-events", - "guides/usecases/manage-notifications-in-multiple-languages", - "guides/usecases/send-notifications-at-any-scale", - "guides/usecases/debug-notifications", - "guides/usecases/centralized-multiple-legacy-implementations" - ] + "group": "Headless", + "icon": "code", + "pages": ["inbox/headless/get-started", "inbox/headless/api-reference"] }, + { - "group": "Demos", + "group": "Other Frameworks", "pages": [ - "guides/demos/introduction" + "inbox/client/vue", + "inbox/client/angular", + "inbox/client/web-component" ] }, { - "group": "Security & Compliance", - "pages": [ - "security-and-compliance/security" - ] + "group": "Deployment", + "pages": ["deployment/production", "deployment/cli", "deployment/actions"] }, + { "group": "Community", "pages": [ + "community/deploy-with-docker", "community/code-of-conduct", "community/get-involved", "community/changelog", @@ -382,51 +168,71 @@ ] }, { - "group": "SDKs & Client Libraries", + "group": "How To", "pages": [ - "sdks/introduction", + "how-to/introduction", { - "group": "Client", + "group": "Workflow Examples", "pages": [ - "sdks/vue", - "sdks/react", - "sdks/angular", - "sdks/web-component", - "sdks/iframe-embed", - "sdks/headless-javascript-service" + "how-to/otp", + "how-to/password-reset", + "how-to/recent-login", + "how-to/invoice-receipt", + "how-to/shipping-confirmation", + "how-to/feedback-reviews" ] - }, + } + ] + }, + { + "group": "SDKs & Client Libraries", + "group": "Framework Typescript SDK", + "pages": [ + "sdks/framework/typescript/overview", + "sdks/framework/typescript/client", + "sdks/framework/typescript/workflow", { - "group": "Server", + "group": "Steps", "pages": [ - "sdks/nodejs", - "sdks/php", - "sdks/laravel", - "sdks/python", - "sdks/kotlin", - "sdks/java", - "sdks/ruby", - "sdks/go", - "sdks/dotnet" + "sdks/framework/typescript/steps/introduction", + "sdks/framework/typescript/steps/email", + "sdks/framework/typescript/steps/inbox", + "sdks/framework/typescript/steps/sms", + "sdks/framework/typescript/steps/push", + "sdks/framework/typescript/steps/chat", + "sdks/framework/typescript/steps/digest", + "sdks/framework/typescript/steps/delay", + "sdks/framework/typescript/steps/custom" ] } ] }, + { + "group": "Rest API SDK", + "pages": [ + "sdks/nodejs", + "sdks/php", + "sdks/laravel", + "sdks/python", + "sdks/kotlin", + "sdks/java", + "sdks/ruby", + "sdks/go", + "sdks/dotnet" + ] + }, { "group": "Additional Resources", "pages": [ + "additional-resources/security", "additional-resources/idempotency", "additional-resources/data-migrations", - "additional-resources/glossary", - "additional-resources/posts-videos-and-articles" + "additional-resources/glossary" ] }, { "group": "API Reference", - "pages": [ - "api-reference/overview", - "api-reference/rate-limiting" - ] + "pages": ["api-reference/overview", "api-reference/rate-limiting"] }, { "group": "Events", @@ -566,9 +372,7 @@ }, { "group": "Execution Details", - "pages": [ - "api-reference/execution-details/get-execution-details" - ] + "pages": ["api-reference/execution-details/get-execution-details"] }, { "group": "Feeds", @@ -616,101 +420,121 @@ "api-reference/get-v1blueprints" ] }, + { - "group": "Help", - "pages": [ - "help/overview", - "help/account-management", - "help/user-management", - { - "group": "Channels", - "icon": "bars", - "pages": [ - "help/channels/in-app", - "help/channels/email", - "help/channels/sms", - "help/channels/push", - "help/channels/chat" - ] - }, - "help/orchestration", - "help/content-management", - "help/observability", - "help/others", - "help/faq" - ] - }, - { - "group": "Getting Started", - "pages": [ - "framework/quickstart", - "framework/concepts/studio", - "framework/roadmap" - ] - }, - { - "group": "Concepts", - "pages": [ - "framework/concepts/endpoint", - "framework/concepts/workflows", - "framework/concepts/inputs", - "framework/concepts/payload" - ] - }, - { - "group": "Steps", + "group": "Content", "pages": [ - "framework/steps/introduction", - "framework/steps/email", - "framework/steps/in-app", - "framework/steps/sms", - "framework/steps/push", - "framework/steps/chat", - "framework/steps/digest", - "framework/steps/delay", - "framework/steps/custom" + "integrations/content/react-email", + "integrations/content/vue-email", + "integrations/content/svelte-email", + "integrations/content/remix-react-email" ] }, { - "group": "SDK", - "pages": [ - "framework/sdk/client", - "framework/sdk/frameworks/nextjs", - "framework/sdk/frameworks/nuxt", - "framework/sdk/frameworks/express", - "framework/sdk/frameworks/remix", - "framework/sdk/frameworks/sveltekit", - "framework/sdk/frameworks/h3", - "framework/sdk/typescript" - ] + "group": "Schema", + "pages": ["integrations/schema/zod"] }, { - "group": "Deployment", + "group": "Providers", "pages": [ - "framework/deployment/production", - "framework/deployment/cli", - "framework/deployment/actions" + "integrations/providers/introduction", + "integrations/providers/default-providers", + { + "group": "Email", + "icon": "envelope", + "pages": [ + "integrations/providers/email/overview", + "integrations/providers/email/infobip", + "integrations/providers/email/amazon-ses", + "integrations/providers/email/braze", + "integrations/providers/email/mailersend", + "integrations/providers/email/mailgun", + "integrations/providers/email/mailjet", + "integrations/providers/email/mailtrap", + "integrations/providers/email/mandrill", + "integrations/providers/email/netcore", + "integrations/providers/email/outlook365", + "integrations/providers/email/postmark", + "integrations/providers/email/resend", + "integrations/providers/email/sendgrid", + "integrations/providers/email/sendinblue", + "integrations/providers/email/sparkpost", + "integrations/providers/email/plunk", + "integrations/providers/email/custom-smtp" + ] + }, + { + "group": "SMS", + "icon": "message-sms", + "pages": [ + "integrations/providers/sms/overview", + "integrations/providers/sms/twilio", + "integrations/providers/sms/sms77", + "integrations/providers/sms/africas-talking", + "integrations/providers/sms/azure", + "integrations/providers/sms/bulk-sms", + "integrations/providers/sms/messagebird", + "integrations/providers/sms/simpletexting", + "integrations/providers/sms/infobip", + "integrations/providers/sms/sendchamp", + "integrations/providers/sms/aws-sns", + "integrations/providers/sms/nexmo", + "integrations/providers/sms/plivo", + "integrations/providers/sms/telnyx", + "integrations/providers/sms/termii", + "integrations/providers/sms/firetext", + "integrations/providers/sms/gupshup", + "integrations/providers/sms/clickatell" + ] + }, + { + "group": "Push", + "icon": "mobile", + "pages": [ + "integrations/providers/push/overview", + "integrations/providers/push/fcm", + "integrations/providers/push/apns", + "integrations/providers/push/expo-push", + "integrations/providers/push/onesignal", + "integrations/providers/push/pushpad", + "integrations/providers/push/push-webhook", + "integrations/providers/push/pusher-beams" + ] + }, + { + "group": "Chat", + "icon": "comments", + "pages": [ + "integrations/providers/chat/overview", + "integrations/providers/chat/discord", + "integrations/providers/chat/slack", + "integrations/providers/chat/ms-teams", + "integrations/providers/chat/zulip", + "integrations/providers/chat/whats-app" + ] + } ] }, { - "group": "For Non-Developers", - "pages": [ - "framework/non-developers/introduction" - ] + "group": "Recipes", + "pages": ["recipes/json-schema"] }, { - "group": "Integrations", + "group": "Workflow Examples", "pages": [ - "framework/integrations/react-email", - "framework/integrations/vue-email", - "framework/integrations/svelte-email", - "framework/integrations/remix-react-email" + "guides/workflows/introduction", + "guides/workflows/otp", + "guides/workflows/password-reset", + "guides/workflows/recent-login", + "guides/workflows/invoice-receipt", + "guides/workflows/shipping-confirmation", + "guides/workflows/feedback-reviews" ] }, { - "group": "Recipes", + "group": "Integration Guides", "pages": [ - "framework/recipes/json-schema" + "guides/integrations/segment" ] } ], @@ -724,6 +548,10 @@ "source": "/framework/introduction", "destination": "/framework/quickstart" }, + { + "source": "/echo/quickstart", + "destination": "/getting-started/introduction" + }, { "source": "/echo/:slug*", "destination": "/framework/:slug*" @@ -751,6 +579,70 @@ { "source": "/guides/echo-guides/echo-svelte", "destination": "/guides/framework-guides/framework-svelte" + }, + { + "source": "/channels-and-providers/push/fcm", + "destination": "/integrations/providers/push/fcm" + }, + { + "source": "/self-hosting-novu/deploy-with-docker", + "destination": "/community/deploy-with-docker" + }, + { + "source": "/notification-center/client/react/api-reference", + "destination": "/inbox/react/api-reference" + }, + { + "source": "/subscribers/subscribers", + "destination": "/concepts/subscribers" + }, + { + "source": "/quickstarts/react", + "destination": "/inbox/react/get-started" + }, + { + "source": "/notification-center/client/react/customization", + "destination": "/inbox/react/customization" + }, + { + "source": "/channels-and-providers/push/overview", + "destination": "/integrations/providers/push/overview" + }, + { + "source": "/workflows/notification-workflows", + "destination": "/concepts/workflows" + }, + { + "source": "/channels-and-providers/sms/nexmo", + "destination": "/integrations/providers/sms/nexmo" + }, + { + "source": "/channels-and-providers/:slug*", + "destination": "/integrations/providers/:slug*" + }, + { + "source": "/notification-center/introduction", + "destination": "/inbox/introduction" + }, + { + "source": "/sdks/introduction", + "destination": "/sdks/framework/typescript/overview" + }, + { + "source": "/subscribers/topics", + "destination": "/concepts/topics" + }, + { + "source": "/platform/environments", + "destination": "/concepts/environments" + }, + { + "source": "/tenants/introduction", + "destination": "/concepts/tenants" + }, + { + "source": "/guides/how-to-integrate-segment-with-novu", + "destination": "/guides/integrations/segment" } ], "footerSocials": { @@ -759,10 +651,7 @@ "linkedin": "https://www.linkedin.com/company/novuco" }, "api": { - "baseUrl": [ - "https://api.novu.co", - "https://eu.api.novu.co" - ], + "baseUrl": ["https://api.novu.co", "https://eu.api.novu.co"], "auth": { "method": "key", "name": "Authorization", @@ -778,12 +667,11 @@ } }, "versions": [ - "v0.23.0", - "v0.22.0", - "v0.21.0", - "v0.20.0", - "v0.19.0", - "v0.18.0" + "v2.x", + { + "name": "v0.x", + "url": "https://v0.x-docs.novu.co" + } ], "metadata": { "og:image": "https://novu.co/images/social-preview.jpg" diff --git a/openapi.json b/openapi.json index ae267b23..52969393 100644 --- a/openapi.json +++ b/openapi.json @@ -1 +1,8905 @@ -{"openapi": "3.0.0", "paths": {"/v1/organizations": {"post": {"operationId": "OrganizationController_createOrganization", "summary": "Create an organization", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CreateOrganizationDto"}}}}, "responses": {"201": {"description": "Created", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/OrganizationResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Organizations"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "OrganizationController_createOrganization", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.organizations.create({\n name: \"\",\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}, "get": {"operationId": "OrganizationController_listOrganizations", "summary": "Fetch all organizations", "parameters": [], "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/OrganizationResponseDto"}}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Organizations"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "OrganizationController_listOrganizations", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.organizations.list();\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}, "patch": {"operationId": "OrganizationController_rename", "x-speakeasy-name-override": "rename", "summary": "Rename organization name", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/RenameOrganizationDto"}}}}, "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/RenameOrganizationDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Organizations"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "OrganizationController_rename", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.organizations.rename({\n name: \"\",\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/organizations/me": {"get": {"operationId": "OrganizationController_getSelfOrganizationData", "summary": "Fetch current organization details", "parameters": [], "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/OrganizationResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Organizations"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "OrganizationController_getSelfOrganizationData", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.organizations.retrieve();\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/organizations/members/{memberId}": {"delete": {"operationId": "OrganizationController_remove", "x-speakeasy-group": "Organizations.Members", "summary": "Remove a member from organization using memberId", "parameters": [{"name": "memberId", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MemberResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Organizations"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "OrganizationController_remove", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.organizations.members.delete(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/organizations/members": {"get": {"operationId": "OrganizationController_listOrganizationMembers", "x-speakeasy-group": "Organizations.Members", "summary": "Fetch all members of current organizations", "parameters": [], "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/MemberResponseDto"}}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Organizations"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "OrganizationController_listOrganizationMembers", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.organizations.members.list();\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/organizations/branding": {"put": {"operationId": "OrganizationController_updateBrandingDetails", "x-speakeasy-group": "Organizations.Branding", "summary": "Update organization branding details", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UpdateBrandingDetailsDto"}}}}, "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/OrganizationBrandingResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Organizations"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "OrganizationController_updateBrandingDetails", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.organizations.branding.update({\n logo: \"\",\n color: \"fuchsia\",\n fontColor: \"\",\n contentBackground: \"\",\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/environments/me": {"get": {"operationId": "EnvironmentsController_getCurrentEnvironment", "summary": "Get current environment", "parameters": [], "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/EnvironmentResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Environments"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "EnvironmentsController_getCurrentEnvironment", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.environments.retrieve();\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/environments": {"get": {"operationId": "EnvironmentsController_listMyEnvironments", "summary": "Get environments", "parameters": [], "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/EnvironmentResponseDto"}}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Environments"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "EnvironmentsController_listMyEnvironments", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.environments.list();\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/environments/api-keys": {"get": {"operationId": "EnvironmentsController_listOrganizationApiKeys", "x-speakeasy-group": "Environments.ApiKeys", "summary": "Get api keys", "parameters": [], "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/ApiKey"}}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Environments"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "EnvironmentsController_listOrganizationApiKeys", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.environments.apiKeys.list();\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/environments/api-keys/regenerate": {"post": {"operationId": "EnvironmentsController_regenerateOrganizationApiKeys", "x-speakeasy-name-override": "regenerate", "x-speakeasy-group": "Environments.ApiKeys", "summary": "Regenerate api keys", "parameters": [], "responses": {"201": {"description": "Created", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/ApiKey"}}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Environments"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "EnvironmentsController_regenerateOrganizationApiKeys", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.environments.apiKeys.regenerate();\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/execution-details": {"get": {"operationId": "ExecutionDetailsController_getExecutionDetailsForNotification", "summary": "Get execution details", "parameters": [{"name": "notificationId", "required": true, "in": "query", "schema": {"type": "string"}}, {"name": "subscriberId", "required": true, "in": "query", "schema": {"type": "string"}}], "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/ExecutionDetailsResponseDto"}}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Execution Details"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "ExecutionDetailsController_getExecutionDetailsForNotification", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.executionDetails.retrieve(\"\", \"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/workflows": {"get": {"operationId": "WorkflowController_listWorkflows", "summary": "Get workflows", "description": "Workflows were previously named notification templates", "parameters": [{"name": "page", "required": false, "in": "query", "schema": {"type": "number"}}, {"name": "limit", "required": false, "in": "query", "schema": {"maximum": 100, "default": 10, "type": "number"}}, {"name": "query", "required": false, "in": "query", "description": "A query string to filter the results. It allows filtering based on either the name or trigger identifier of the workflow items.", "schema": {"type": "string"}}], "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/WorkflowsResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Workflows"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "WorkflowController_listWorkflows", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.workflows.list({});\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}, "post": {"operationId": "WorkflowController_create", "summary": "Create workflow", "description": "Workflow was previously named notification template", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CreateWorkflowRequestDto"}}}}, "responses": {"201": {"description": "Created", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/WorkflowResponse"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Workflows"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "WorkflowController_create", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.workflows.create({\n name: \"\",\n notificationGroupId: \"\",\n steps: [\n {},\n ],\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/workflows/{workflowId}": {"put": {"operationId": "WorkflowController_updateWorkflowById", "summary": "Update workflow", "description": "Workflow was previously named notification template", "parameters": [{"name": "workflowId", "required": true, "in": "path", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UpdateWorkflowRequestDto"}}}}, "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/WorkflowResponse"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Workflows"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "WorkflowController_updateWorkflowById", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.workflows.update(\"\", {\n name: \"\",\n notificationGroupId: \"\",\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}, "delete": {"operationId": "WorkflowController_deleteWorkflowById", "summary": "Delete workflow", "description": "Workflow was previously named notification template", "parameters": [{"name": "workflowId", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/DataBooleanDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Workflows"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "WorkflowController_deleteWorkflowById", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.workflows.delete(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}, "get": {"operationId": "WorkflowController_getWorkflowById", "summary": "Get workflow", "description": "Workflow was previously named notification template", "parameters": [{"name": "workflowId", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/WorkflowResponse"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Workflows"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "WorkflowController_getWorkflowById", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.workflows.retrieve(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/workflows/variables": {"get": {"operationId": "WorkflowController_getWorkflowVariables", "x-speakeasy-group": "Workflows.Variables", "summary": "Get available variables", "description": "Get the variables that can be used in the workflow", "parameters": [], "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/VariablesResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Workflows"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "WorkflowController_getWorkflowVariables", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.workflows.variables.retrieve();\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/workflows/{workflowId}/status": {"put": {"operationId": "WorkflowController_updateActiveStatus", "x-speakeasy-group": "Workflows.Status", "summary": "Update workflow status", "description": "Workflow was previously named notification template", "parameters": [{"name": "workflowId", "required": true, "in": "path", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ChangeWorkflowStatusRequestDto"}}}}, "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/WorkflowResponse"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Workflows"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "WorkflowController_updateActiveStatus", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.workflows.status.update(\"\", {\n active: false,\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/events/trigger": {"post": {"operationId": "EventsController_trigger", "x-speakeasy-group": "", "x-speakeasy-usage-example": {"title": "Trigger Notification Event"}, "x-speakeasy-name-override": "trigger", "summary": "Trigger event", "description": "\n Trigger event is the main (and only) way to send notifications to subscribers. \n The trigger identifier is used to match the particular workflow associated with it. \n Additional information can be passed according the body interface below.\n ", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/TriggerEventRequestDto"}}}}, "responses": {"201": {"description": "Created", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/TriggerEventResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Events"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "EventsController_trigger", "source": "import { Novu } from \"@novu/api\";\nimport { TopicPayloadDtoType } from \"@novu/api/models/components\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.trigger({\n name: \"workflow_identifier\",\n payload: {},\n overrides: {},\n to: [\n {\n topicKey: \"topic_key\",\n type: TopicPayloadDtoType.Topic,\n },\n ],\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/events/trigger/bulk": {"post": {"operationId": "EventsController_triggerBulk", "x-speakeasy-name-override": "triggerBulk", "summary": "Bulk trigger event", "description": "\n Using this endpoint you can trigger multiple events at once, to avoid multiple calls to the API.\n The bulk API is limited to 100 events per request.\n ", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/BulkTriggerEventDto"}}}}, "responses": {"201": {"description": "Created", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/TriggerEventResponseDto"}}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Events"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "EventsController_triggerBulk", "source": "import { Novu } from \"@novu/api\";\nimport { TopicPayloadDtoType } from \"@novu/api/models/components\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.events.triggerBulk({\n events: [\n {\n name: \"workflow_identifier\",\n payload: {},\n overrides: {},\n to: [\n {\n topicKey: \"topic_key\",\n type: TopicPayloadDtoType.Topic,\n },\n ],\n },\n ],\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/events/trigger/broadcast": {"post": {"operationId": "EventsController_broadcastEventToAll", "x-speakeasy-name-override": "triggerBroadcast", "summary": "Broadcast event to all", "description": "Trigger a broadcast event to all existing subscribers, could be used to send announcements, etc.\n In the future could be used to trigger events to a subset of subscribers based on defined filters.", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/TriggerEventToAllRequestDto"}}}}, "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/TriggerEventResponseDto"}}}}, "201": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/TriggerEventResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Events"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "EventsController_broadcastEventToAll", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.events.triggerBroadcast({\n name: \"\",\n payload: {},\n overrides: {},\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/events/trigger/{transactionId}": {"delete": {"operationId": "EventsController_cancel", "x-speakeasy-name-override": "cancel", "summary": "Cancel triggered event", "description": "\n Using a previously generated transactionId during the event trigger,\n will cancel any active or pending workflows. This is useful to cancel active digests, delays etc...\n ", "parameters": [{"name": "transactionId", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/DataBooleanDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Events"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "EventsController_cancel", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.events.cancel(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/notifications": {"get": {"operationId": "NotificationsController_listNotifications", "summary": "Get notifications", "parameters": [{"name": "channels", "required": true, "in": "query", "schema": {"type": "array", "items": {"type": "string", "enum": ["in_app", "email", "sms", "chat", "push"]}}}, {"name": "templates", "required": true, "in": "query", "schema": {"type": "array", "items": {"type": "string"}}}, {"name": "emails", "required": true, "in": "query", "schema": {"type": "array", "items": {"type": "string"}}}, {"name": "search", "required": true, "in": "query", "deprecated": true, "schema": {"type": "string"}}, {"name": "subscriberIds", "required": true, "in": "query", "schema": {"type": "array", "items": {"type": "string"}}}, {"name": "page", "required": false, "in": "query", "schema": {"default": 0, "type": "number"}}, {"name": "transactionId", "required": false, "in": "query", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ActivitiesResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Notifications"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "NotificationsController_listNotifications", "source": "import { Novu } from \"@novu/api\";\nimport { Channels } from \"@novu/api/models/operations\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.notifications.list({\n channels: [\n Channels.Chat,\n ],\n templates: [\n \"\",\n ],\n emails: [\n \"\",\n ],\n search: \"\",\n subscriberIds: [\n \"\",\n ],\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/notifications/stats": {"get": {"operationId": "NotificationsController_getActivityStats", "x-speakeasy-group": "Notifications.Stats", "summary": "Get notification statistics", "parameters": [], "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ActivityStatsResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Notifications"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "NotificationsController_getActivityStats", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.notifications.stats.retrieve();\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/notifications/graph/stats": {"get": {"operationId": "NotificationsController_getActivityGraphStats", "x-speakeasy-name-override": "graph", "x-speakeasy-group": "Notifications.Stats", "summary": "Get notification graph statistics", "parameters": [{"name": "days", "required": false, "in": "query", "schema": {"type": "number"}}], "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/ActivityGraphStatesResponse"}}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Notifications"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "NotificationsController_getActivityGraphStats", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.notifications.stats.graph(4018.61);\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/notifications/{notificationId}": {"get": {"operationId": "NotificationsController_getNotification", "summary": "Get notification", "parameters": [{"name": "notificationId", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ActivityNotificationResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Notifications"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "NotificationsController_getNotification", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.notifications.retrieve(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/notification-groups": {"post": {"operationId": "NotificationGroupsController_createNotificationGroup", "summary": "Create workflow group", "description": "workflow group was previously named notification group", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CreateNotificationGroupRequestDto"}}}}, "responses": {"201": {"description": "Created", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/NotificationGroupResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Workflow groups"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "NotificationGroupsController_createNotificationGroup", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.workflowGroups.create({\n name: \"\",\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}, "get": {"operationId": "NotificationGroupsController_listNotificationGroups", "summary": "Get workflow groups", "description": "workflow group was previously named notification group", "parameters": [], "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/NotificationGroupResponseDto"}}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Workflow groups"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "NotificationGroupsController_listNotificationGroups", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.workflowGroups.list();\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/notification-groups/{id}": {"get": {"operationId": "NotificationGroupsController_getNotificationGroup", "summary": "Get workflow group", "description": "workflow group was previously named notification group", "parameters": [{"name": "id", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/NotificationGroupResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Workflow groups"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "NotificationGroupsController_getNotificationGroup", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.workflowGroups.retrieve(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}, "patch": {"operationId": "NotificationGroupsController_updateNotificationGroup", "summary": "Update workflow group", "description": "workflow group was previously named notification group", "parameters": [{"name": "id", "required": true, "in": "path", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CreateNotificationGroupRequestDto"}}}}, "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/NotificationGroupResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Workflow groups"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "NotificationGroupsController_updateNotificationGroup", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.workflowGroups.update(\"\", {\n name: \"\",\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}, "delete": {"operationId": "NotificationGroupsController_deleteNotificationGroup", "summary": "Delete workflow group", "description": "workflow group was previously named notification group", "parameters": [{"name": "id", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/DeleteNotificationGroupResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Workflow groups"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "NotificationGroupsController_deleteNotificationGroup", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.workflowGroups.delete(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/integrations": {"get": {"operationId": "IntegrationsController_listIntegrations", "summary": "Get integrations", "description": "Return all the integrations the user has created for that organization. Review v.0.17.0 changelog for a breaking change", "parameters": [], "responses": {"200": {"description": "The list of integrations belonging to the organization that are successfully returned.", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/IntegrationResponseDto"}}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Integrations"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "IntegrationsController_listIntegrations", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.integrations.list();\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}, "post": {"operationId": "IntegrationsController_createIntegration", "summary": "Create integration", "description": "Create an integration for the current environment the user is based on the API key provided", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CreateIntegrationRequestDto"}}}}, "responses": {"201": {"description": "Created", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/IntegrationResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Integrations"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "IntegrationsController_createIntegration", "source": "import { Novu } from \"@novu/api\";\nimport { CreateIntegrationRequestDtoChannel } from \"@novu/api/models/components\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.integrations.create({\n providerId: \"\",\n channel: CreateIntegrationRequestDtoChannel.Sms,\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/integrations/active": {"get": {"operationId": "IntegrationsController_getActiveIntegrations", "x-speakeasy-name-override": "listActive", "summary": "Get active integrations", "description": "Return all the active integrations the user has created for that organization. Review v.0.17.0 changelog for a breaking change", "parameters": [], "responses": {"200": {"description": "The list of active integrations belonging to the organization that are successfully returned.", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/IntegrationResponseDto"}}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Integrations"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "IntegrationsController_getActiveIntegrations", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.integrations.listActive();\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/integrations/webhook/provider/{providerOrIntegrationId}/status": {"get": {"operationId": "IntegrationsController_getWebhookSupportStatus", "x-speakeasy-group": "Integrations.Webhooks", "summary": "Get webhook support status for provider", "description": "Return the status of the webhook for this provider, if it is supported or if it is not based on a boolean value", "parameters": [{"name": "providerOrIntegrationId", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"200": {"description": "The status of the webhook for the provider requested", "content": {"application/json": {"schema": {"type": "boolean"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Integrations"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "IntegrationsController_getWebhookSupportStatus", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.integrations.webhooks.retrieve(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/integrations/{integrationId}": {"put": {"operationId": "IntegrationsController_updateIntegrationById", "summary": "Update integration", "parameters": [{"name": "integrationId", "required": true, "in": "path", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UpdateIntegrationRequestDto"}}}}, "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/IntegrationResponseDto"}}}}, "404": {"description": "The integration with the integrationId provided does not exist in the database."}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Integrations"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "IntegrationsController_updateIntegrationById", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.integrations.update(\"\", {});\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}, "delete": {"operationId": "IntegrationsController_removeIntegration", "summary": "Delete integration", "parameters": [{"name": "integrationId", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/IntegrationResponseDto"}}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Integrations"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "IntegrationsController_removeIntegration", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.integrations.delete(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/integrations/{integrationId}/set-primary": {"post": {"operationId": "IntegrationsController_setIntegrationAsPrimary", "x-speakeasy-name-override": "setAsPrimary", "summary": "Set integration as primary", "parameters": [{"name": "integrationId", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/IntegrationResponseDto"}}}}, "201": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/IntegrationResponseDto"}}}}, "404": {"description": "The integration with the integrationId provided does not exist in the database."}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Integrations"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "IntegrationsController_setIntegrationAsPrimary", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.integrations.setAsPrimary(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/changes": {"get": {"operationId": "ChangesController_getChanges", "summary": "Get changes", "parameters": [{"name": "page", "required": false, "in": "query", "schema": {"type": "number"}}, {"name": "limit", "required": false, "in": "query", "schema": {"maximum": 100, "default": 10, "type": "number"}}, {"name": "promoted", "required": true, "in": "query", "schema": {"default": "false", "type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ChangesResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Changes"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "ChangesController_getChanges", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.changes.retrieve({\n promoted: \"false\",\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/changes/count": {"get": {"operationId": "ChangesController_getChangesCount", "x-speakeasy-name-override": "count", "summary": "Get changes count", "parameters": [], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/DataNumberDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Changes"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "ChangesController_getChangesCount", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.changes.count();\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/changes/bulk/apply": {"post": {"operationId": "ChangesController_bulkApplyDiff", "x-speakeasy-name-override": "applyBulk", "summary": "Apply changes", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/BulkApplyChangeDto"}}}}, "responses": {"201": {"description": "Created", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/ChangeResponseDto"}}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Changes"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "ChangesController_bulkApplyDiff", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.changes.applyBulk({\n changeIds: [\n \"\",\n ],\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/changes/{changeId}/apply": {"post": {"operationId": "ChangesController_applyDiff", "x-speakeasy-name-override": "apply", "summary": "Apply change", "parameters": [{"name": "changeId", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"201": {"description": "Created", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/ChangeResponseDto"}}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Changes"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "ChangesController_applyDiff", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.changes.apply(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/subscribers": {"get": {"operationId": "SubscribersController_listSubscribers", "x-speakeasy-pagination": {"type": "offsetLimit", "inputs": [{"name": "page", "in": "parameters", "type": "page"}, {"name": "limit", "in": "parameters", "type": "limit"}], "outputs": {"results": "$.data.resultArray"}}, "summary": "Get subscribers", "description": "Returns a list of subscribers, could paginated using the `page` and `limit` query parameter", "parameters": [{"name": "page", "required": false, "in": "query", "schema": {"type": "number"}}, {"name": "limit", "required": false, "in": "query", "schema": {"maximum": 100, "default": 10, "type": "number"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"allOf": [{"$ref": "#/components/schemas/PaginatedResponseDto"}, {"properties": {"data": {"type": "array", "items": {"$ref": "#/components/schemas/SubscriberResponseDto"}}}}]}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Subscribers"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "SubscribersController_listSubscribers", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.list(7685.78, 10);\n\n for await (const page of result) {\n // handle page\n }\n}\n\nrun();"}]}, "post": {"operationId": "SubscribersController_createSubscriber", "summary": "Create subscriber", "description": "Creates a subscriber entity, in the Novu platform. The subscriber will be later used to receive notifications, and access notification feeds. Communication credentials such as email, phone number, and 3 rd party credentials i.e slack tokens could be later associated to this entity.", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CreateSubscriberRequestDto"}}}}, "responses": {"201": {"description": "Created", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SubscriberResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Subscribers"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "SubscribersController_createSubscriber", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.create({\n subscriberId: \"\",\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/subscribers/{subscriberId}": {"get": {"operationId": "SubscribersController_getSubscriber", "summary": "Get subscriber", "description": "Get subscriber by your internal id used to identify the subscriber", "parameters": [{"name": "subscriberId", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SubscriberResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Subscribers"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "SubscribersController_getSubscriber", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.retrieve(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}, "put": {"operationId": "SubscribersController_updateSubscriber", "summary": "Update subscriber", "description": "Used to update the subscriber entity with new information", "parameters": [{"name": "subscriberId", "required": true, "in": "path", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UpdateSubscriberRequestDto"}}}}, "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SubscriberResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Subscribers"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "SubscribersController_updateSubscriber", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.update(\"\", {});\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}, "delete": {"operationId": "SubscribersController_removeSubscriber", "summary": "Delete subscriber", "description": "Deletes a subscriber entity from the Novu platform", "parameters": [{"name": "subscriberId", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/DeleteSubscriberResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Subscribers"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "SubscribersController_removeSubscriber", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.delete(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/subscribers/bulk": {"post": {"operationId": "SubscribersController_bulkCreateSubscribers", "x-speakeasy-name-override": "createBulk", "summary": "Bulk create subscribers", "description": "\n Using this endpoint you can create multiple subscribers at once, to avoid multiple calls to the API.\n The bulk API is limited to 500 subscribers per request.\n ", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/BulkSubscriberCreateDto"}}}}, "responses": {"201": {"description": ""}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Subscribers"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "SubscribersController_bulkCreateSubscribers", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n await novu.subscribers.createBulk({\n subscribers: [\n {\n subscriberId: \"\",\n },\n ],\n });\n\n \n}\n\nrun();"}]}}, "/v1/subscribers/{subscriberId}/credentials": {"put": {"operationId": "SubscribersController_updateSubscriberChannel", "x-speakeasy-group": "Subscribers.Credentials", "summary": "Update subscriber credentials", "description": "Subscriber credentials associated to the delivery methods such as slack and push tokens.", "parameters": [{"name": "subscriberId", "required": true, "in": "path", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UpdateSubscriberChannelRequestDto"}}}}, "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SubscriberResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Subscribers"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "SubscribersController_updateSubscriberChannel", "source": "import { Novu } from \"@novu/api\";\nimport { UpdateSubscriberChannelRequestDtoProviderId } from \"@novu/api/models/components\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.credentials.update(\"\", {\n providerId: UpdateSubscriberChannelRequestDtoProviderId.Pushpad,\n credentials: {\n webhookUrl: \"\",\n },\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}, "patch": {"operationId": "SubscribersController_modifySubscriberChannel", "x-speakeasy-name-override": "append", "x-speakeasy-group": "Subscribers.Credentials", "summary": "Modify subscriber credentials", "description": "Subscriber credentials associated to the delivery methods such as slack and push tokens.\n This endpoint appends provided credentials and deviceTokens to the existing ones.", "parameters": [{"name": "subscriberId", "required": true, "in": "path", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UpdateSubscriberChannelRequestDto"}}}}, "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SubscriberResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Subscribers"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "SubscribersController_modifySubscriberChannel", "source": "import { Novu } from \"@novu/api\";\nimport { UpdateSubscriberChannelRequestDtoProviderId } from \"@novu/api/models/components\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.credentials.append(\"\", {\n providerId: UpdateSubscriberChannelRequestDtoProviderId.Zulip,\n credentials: {\n webhookUrl: \"\",\n },\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/subscribers/{subscriberId}/credentials/{providerId}": {"delete": {"operationId": "SubscribersController_deleteSubscriberCredentials", "x-speakeasy-group": "Subscribers.Credentials", "summary": "Delete subscriber credentials by providerId", "description": "Delete subscriber credentials such as slack and expo tokens.", "parameters": [{"name": "subscriberId", "required": true, "in": "path", "schema": {"type": "string"}}, {"name": "providerId", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"204": {"description": ""}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Subscribers"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "SubscribersController_deleteSubscriberCredentials", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n await novu.subscribers.credentials.delete(\"\", \"\");\n\n \n}\n\nrun();"}]}}, "/v1/subscribers/{subscriberId}/online-status": {"patch": {"operationId": "SubscribersController_updateSubscriberOnlineFlag", "x-speakeasy-name-override": "updateOnlineFlag", "x-speakeasy-group": "Subscribers.properties", "summary": "Update subscriber online status", "description": "Used to update the subscriber isOnline flag.", "parameters": [{"name": "subscriberId", "required": true, "in": "path", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UpdateSubscriberOnlineFlagRequestDto"}}}}, "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SubscriberResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Subscribers"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "SubscribersController_updateSubscriberOnlineFlag", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.properties.updateOnlineFlag(\"\", {\n isOnline: false,\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/subscribers/{subscriberId}/preferences": {"get": {"operationId": "SubscribersController_listSubscriberPreferences", "x-speakeasy-group": "Subscribers.Preferences", "summary": "Get subscriber preferences", "parameters": [{"name": "subscriberId", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/UpdateSubscriberPreferenceResponseDto"}}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Subscribers"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "SubscribersController_listSubscriberPreferences", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.preferences.list(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}, "patch": {"operationId": "SubscribersController_updateSubscriberGlobalPreferences", "x-speakeasy-name-override": "updateGlobal", "x-speakeasy-group": "Subscribers.Preferences", "summary": "Update subscriber global preferences", "parameters": [{"name": "subscriberId", "required": true, "in": "path", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UpdateSubscriberGlobalPreferencesRequestDto"}}}}, "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UpdateSubscriberPreferenceResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Subscribers"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "SubscribersController_updateSubscriberGlobalPreferences", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.preferences.updateGlobal(\"\", {});\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/subscribers/{subscriberId}/preferences/{parameter}": {"get": {"operationId": "SubscribersController_getSubscriberPreferenceByLevel", "x-speakeasy-name-override": "retrieveByLevel", "x-speakeasy-group": "Subscribers.Preferences", "summary": "Get subscriber preferences by level", "parameters": [{"name": "parameter", "required": true, "in": "path", "schema": {"type": "string"}}, {"name": "subscriberId", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/GetSubscriberPreferencesResponseDto"}}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Subscribers"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "SubscribersController_getSubscriberPreferenceByLevel", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.preferences.retrieveByLevel(\"\", \"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}, "patch": {"operationId": "SubscribersController_updateSubscriberPreference", "x-speakeasy-group": "Subscribers.Preferences", "summary": "Update subscriber preference", "parameters": [{"name": "subscriberId", "required": true, "in": "path", "schema": {"type": "string"}}, {"name": "parameter", "required": true, "in": "path", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UpdateSubscriberPreferenceRequestDto"}}}}, "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UpdateSubscriberPreferenceResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Subscribers"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "SubscribersController_updateSubscriberPreference", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.preferences.update({\n subscriberId: \"\",\n parameter: \"\",\n updateSubscriberPreferenceRequestDto: {},\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/subscribers/{subscriberId}/notifications/feed": {"get": {"operationId": "SubscribersController_getNotificationsFeed", "x-speakeasy-group": "Subscribers.Notifications", "summary": "Get in-app notification feed for a particular subscriber", "parameters": [{"name": "subscriberId", "required": true, "in": "path", "schema": {"type": "string"}}, {"name": "page", "required": false, "in": "query", "schema": {"type": "number"}}, {"name": "limit", "required": false, "in": "query", "schema": {"maximum": 100, "default": 10, "type": "number"}}, {"name": "read", "required": false, "in": "query", "schema": {"type": "boolean"}}, {"name": "seen", "required": false, "in": "query", "schema": {"type": "boolean"}}, {"name": "payload", "required": false, "in": "query", "description": "Base64 encoded string of the partial payload JSON object", "example": "btoa(JSON.stringify({ foo: 123 })) results in base64 encoded string like eyJmb28iOjEyM30=", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"allOf": [{"$ref": "#/components/schemas/PaginatedResponseDto"}, {"properties": {"data": {"type": "array", "items": {"$ref": "#/components/schemas/FeedResponseDto"}}}}]}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Subscribers"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "SubscribersController_getNotificationsFeed", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.notifications.retrieve({\n subscriberId: \"\",\n payload: \"btoa(JSON.stringify({ foo: 123 })) results in base64 encoded string like eyJmb28iOjEyM30=\",\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/subscribers/{subscriberId}/notifications/unseen": {"get": {"operationId": "SubscribersController_getUnseenCount", "x-speakeasy-name-override": "unseenCount", "x-speakeasy-group": "Subscribers.Notifications", "summary": "Get the unseen in-app notifications count for subscribers feed", "parameters": [{"name": "seen", "required": true, "in": "query", "schema": {"type": "boolean"}}, {"name": "subscriberId", "required": true, "in": "path", "schema": {"type": "string"}}, {"name": "limit", "required": true, "in": "query", "schema": {"type": "number"}}], "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UnseenCountResponse"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Subscribers"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "SubscribersController_getUnseenCount", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.notifications.unseenCount({\n seen: false,\n subscriberId: \"\",\n limit: 2166.35,\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/subscribers/{subscriberId}/messages/mark-as": {"post": {"operationId": "SubscribersController_markMessagesAs", "x-speakeasy-name-override": "markAllAs", "x-speakeasy-group": "Subscribers.Messages", "summary": "Mark a subscriber messages as seen, read, unseen or unread", "parameters": [{"name": "subscriberId", "required": true, "in": "path", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MessageMarkAsRequestDto"}}}}, "responses": {"201": {"description": "", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/MessageEntity"}}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Subscribers"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "SubscribersController_markMessagesAs", "source": "import { Novu } from \"@novu/api\";\nimport { MarkAs } from \"@novu/api/models/components\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.messages.markAllAs(\"\", {\n messageId: \"\",\n markAs: MarkAs.Seen,\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/subscribers/{subscriberId}/messages/mark-all": {"post": {"operationId": "SubscribersController_markAllUnreadAsRead", "x-speakeasy-name-override": "markAll", "x-speakeasy-group": "Subscribers.Messages", "summary": "Marks all the subscriber messages as read, unread, seen or unseen. Optionally you can pass feed id (or array) to mark messages of a particular feed.", "parameters": [{"name": "subscriberId", "required": true, "in": "path", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MarkAllMessageAsRequestDto"}}}}, "responses": {"201": {"description": "", "content": {"application/json": {"schema": {"type": "number"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Subscribers"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "SubscribersController_markAllUnreadAsRead", "source": "import { Novu } from \"@novu/api\";\nimport { MarkAllMessageAsRequestDtoMarkAs } from \"@novu/api/models/components\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.messages.markAll(\"\", {\n markAs: MarkAllMessageAsRequestDtoMarkAs.Seen,\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/subscribers/{subscriberId}/messages/{messageId}/actions/{type}": {"post": {"operationId": "SubscribersController_markActionAsSeen", "x-speakeasy-name-override": "updateAsSeen", "x-speakeasy-group": "Subscribers.Messages", "summary": "Mark message action as seen", "parameters": [{"name": "messageId", "required": true, "in": "path", "schema": {"type": "string"}}, {"name": "type", "required": true, "in": "path", "schema": {"type": "string"}}, {"name": "subscriberId", "required": true, "in": "path", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MarkMessageActionAsSeenDto"}}}}, "responses": {"201": {"description": "Created", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MessageResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Subscribers"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "SubscribersController_markActionAsSeen", "source": "import { Novu } from \"@novu/api\";\nimport { MarkMessageActionAsSeenDtoStatus } from \"@novu/api/models/components\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.messages.updateAsSeen({\n messageId: \"\",\n type: \"\",\n subscriberId: \"\",\n markMessageActionAsSeenDto: {\n status: MarkMessageActionAsSeenDtoStatus.Done,\n },\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/subscribers/{subscriberId}/credentials/{providerId}/oauth/callback": {"get": {"operationId": "SubscribersController_chatOauthCallback", "x-speakeasy-name-override": "chatAccessOauthCallBack", "x-speakeasy-group": "Subscribers.Authentication", "summary": "Handle providers oauth redirect", "parameters": [{"name": "subscriberId", "required": true, "in": "path", "schema": {"type": "string"}}, {"name": "providerId", "required": true, "in": "path", "schema": {"type": "string"}}, {"name": "code", "required": true, "in": "query", "schema": {"type": "string"}}, {"name": "hmacHash", "required": true, "in": "query", "schema": {"type": "string"}}, {"name": "environmentId", "required": true, "in": "query", "schema": {"type": "string"}}, {"name": "integrationIdentifier", "required": false, "in": "query", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"type": "object"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Subscribers"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "SubscribersController_chatOauthCallback", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.authentication.chatAccessOauthCallBack({\n subscriberId: \"\",\n providerId: \"\",\n code: \"\",\n hmacHash: \"\",\n environmentId: \"\",\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/subscribers/{subscriberId}/credentials/{providerId}/oauth": {"get": {"operationId": "SubscribersController_chatAccessOauth", "x-speakeasy-name-override": "chatAccessOauth", "x-speakeasy-group": "Subscribers.Authentication", "summary": "Handle chat oauth", "parameters": [{"name": "subscriberId", "required": true, "in": "path", "schema": {"type": "string"}}, {"name": "providerId", "required": true, "in": "path", "schema": {"type": "string"}}, {"name": "hmacHash", "required": true, "in": "query", "schema": {"type": "string"}}, {"name": "environmentId", "required": true, "in": "query", "schema": {"type": "string"}}, {"name": "integrationIdentifier", "required": false, "in": "query", "schema": {"type": "string"}}], "responses": {"200": {"description": ""}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Subscribers"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "SubscribersController_chatAccessOauth", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n await novu.subscribers.authentication.chatAccessOauth({\n subscriberId: \"\",\n providerId: \"\",\n hmacHash: \"\",\n environmentId: \"\",\n });\n\n \n}\n\nrun();"}]}}, "/v1/feeds": {"post": {"operationId": "FeedsController_createFeed", "summary": "Create feed", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CreateFeedRequestDto"}}}}, "responses": {"201": {"description": "Created", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/FeedResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Feeds"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "FeedsController_createFeed", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.feeds.create({\n name: \"\",\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}, "get": {"operationId": "FeedsController_getFeeds", "summary": "Get feeds", "parameters": [], "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/FeedResponseDto"}}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Feeds"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "FeedsController_getFeeds", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.feeds.retrieve();\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/feeds/{feedId}": {"delete": {"operationId": "FeedsController_deleteFeedById", "summary": "Delete feed", "parameters": [{"name": "feedId", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/FeedResponseDto"}}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Feeds"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "FeedsController_deleteFeedById", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.feeds.delete(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/layouts": {"post": {"operationId": "LayoutsController_PropertyDescriptor", "x-speakeasy-name-override": "create", "summary": "Layout creation", "description": "Create a layout", "parameters": [], "responses": {"201": {"description": "Created", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CreateLayoutResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Layouts"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "LayoutsController_PropertyDescriptor", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.layouts.create();\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}, "get": {"operationId": "LayoutsController_listLayouts", "summary": "Filter layouts", "description": "Returns a list of layouts that can be paginated using the `page` query parameter and filtered by the environment where it is executed from the organization the user belongs to.", "parameters": [{"name": "page", "required": false, "in": "query", "description": "Number of page for pagination", "schema": {"minimum": 0, "type": "number"}}, {"name": "pageSize", "required": false, "in": "query", "description": "Size of page for pagination", "schema": {"minimum": 0, "type": "number"}}, {"name": "sortBy", "required": false, "in": "query", "description": "Sort field. Currently only supported `createdAt`", "schema": {"type": "string"}}, {"name": "orderBy", "required": false, "in": "query", "description": "Direction of the sorting query param", "schema": {"enum": ["ASC", "DESC"], "type": "string"}}], "responses": {"200": {"description": "The list of layouts that match the criteria of the query params are successfully returned."}, "400": {"description": "Page size can not be larger than the page size limit."}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Layouts"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "LayoutsController_listLayouts", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n await novu.layouts.list({});\n\n \n}\n\nrun();"}]}}, "/v1/layouts/{layoutId}": {"get": {"operationId": "LayoutsController_getLayout", "summary": "Get layout", "description": "Get a layout by its ID", "parameters": [{"name": "layoutId", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/GetLayoutResponseDto"}}}}, "404": {"description": "The layout with the layoutId provided does not exist in the database."}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Layouts"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "LayoutsController_getLayout", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.layouts.retrieve(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}, "delete": {"operationId": "LayoutsController_deleteLayout", "summary": "Delete layout", "description": "Execute a soft delete of a layout given a certain ID.", "parameters": [{"name": "layoutId", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"204": {"description": "The layout has been deleted correctly"}, "404": {"description": "The layout with the layoutId provided does not exist in the database so it can not be deleted."}, "409": {"description": "Either you are trying to delete a layout that is being used or a layout that is the default in the environment.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Layouts"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "LayoutsController_deleteLayout", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n await novu.layouts.delete(\"\");\n\n \n}\n\nrun();"}]}, "patch": {"operationId": "LayoutsController_updateLayout", "summary": "Update a layout", "description": "Update the name, content and variables of a layout. Also change it to be default or no.", "parameters": [{"name": "layoutId", "required": true, "in": "path", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UpdateLayoutRequestDto"}}}}, "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UpdateLayoutResponseDto"}}}}, "400": {"description": "The payload provided or the URL param are not right."}, "404": {"description": "The layout with the layoutId provided does not exist in the database so it can not be updated."}, "409": {"description": "One default layout is needed. If you are trying to turn a default layout as not default, you should turn a different layout as default first and automatically it will be done by the system.", "content": {"application/json": {"schema": {"example": "One default layout is required"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Layouts"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "LayoutsController_updateLayout", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.layouts.update(\"\", {\n identifier: \"\",\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/layouts/{layoutId}/default": {"post": {"operationId": "LayoutsController_setDefaultLayout", "x-speakeasy-name-override": "setAsDefault", "summary": "Set default layout", "description": "Sets the default layout for the environment and updates to non default to the existing default layout (if any).", "parameters": [{"name": "layoutId", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"204": {"description": "The selected layout has been set as the default for the environment."}, "404": {"description": "The layout with the layoutId provided does not exist in the database so it can not be set as the default for the environment."}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Layouts"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "LayoutsController_setDefaultLayout", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n await novu.layouts.setAsDefault(\"\");\n\n \n}\n\nrun();"}]}}, "/v1/messages": {"get": {"operationId": "MessagesController_getMessages", "summary": "Get messages", "description": "Returns a list of messages, could paginate using the `page` query parameter", "parameters": [{"name": "channel", "required": false, "in": "query", "schema": {"enum": ["in_app", "email", "sms", "chat", "push"], "type": "string"}}, {"name": "subscriberId", "required": false, "in": "query", "schema": {"type": "string"}}, {"name": "transactionId", "required": false, "in": "query", "schema": {"type": "array", "items": {"type": "string"}}}, {"name": "page", "required": false, "in": "query", "schema": {"default": 0, "type": "number"}}, {"name": "limit", "required": false, "in": "query", "schema": {"default": 10, "type": "number"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ActivitiesResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Messages"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "MessagesController_getMessages", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.messages.retrieve({});\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/messages/{messageId}": {"delete": {"operationId": "MessagesController_deleteMessage", "summary": "Delete message", "description": "Deletes a message entity from the Novu platform", "parameters": [{"name": "messageId", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/DeleteMessageResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Messages"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "MessagesController_deleteMessage", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.messages.delete(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/messages/transaction/{transactionId}": {"delete": {"operationId": "MessagesController_deleteMessagesByTransactionId", "x-speakeasy-name-override": "deleteByTransactionId", "summary": "Delete messages by transactionId", "description": "Deletes messages entity from the Novu platform using TransactionId of message", "parameters": [{"name": "channel", "required": false, "in": "query", "description": "The channel of the message to be deleted", "schema": {"enum": ["in_app", "email", "sms", "chat", "push"], "type": "string"}}, {"name": "transactionId", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"204": {"description": ""}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Messages"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "MessagesController_deleteMessagesByTransactionId", "source": "import { Novu } from \"@novu/api\";\nimport { QueryParamChannel } from \"@novu/api/models/operations\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n await novu.messages.deleteByTransactionId(\"\", QueryParamChannel.Push);\n\n \n}\n\nrun();"}]}}, "/v1/topics": {"post": {"operationId": "TopicsController_createTopic", "summary": "Topic creation", "description": "Create a topic", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CreateTopicRequestDto"}}}}, "responses": {"201": {"description": "Created", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CreateTopicResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Topics"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "TopicsController_createTopic", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.topics.create({\n key: \"\",\n name: \"\",\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}, "get": {"operationId": "TopicsController_listTopics", "summary": "Filter topics", "description": "Returns a list of topics that can be paginated using the `page` query parameter and filtered by the topic key with the `key` query parameter", "parameters": [{"name": "page", "required": false, "in": "query", "description": "Number of page for the pagination", "schema": {"minimum": 0, "default": 0, "type": "number"}}, {"name": "pageSize", "required": false, "in": "query", "description": "Size of page for the pagination", "schema": {"minimum": 0, "default": 10, "type": "number"}}, {"name": "key", "required": false, "in": "query", "description": "Topic key", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/FilterTopicsResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Topics"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "TopicsController_listTopics", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.topics.list({});\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/topics/{topicKey}/subscribers": {"post": {"operationId": "TopicsController_addSubscribers", "x-speakeasy-name-override": "assign", "x-speakeasy-group": "Topics.Subscribers", "summary": "Subscribers addition", "description": "Add subscribers to a topic by key", "parameters": [{"name": "topicKey", "required": true, "in": "path", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AddSubscribersRequestDto"}}}}, "responses": {"200": {"description": ""}, "204": {"description": ""}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Topics"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "TopicsController_addSubscribers", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n await novu.topics.subscribers.assign(\"\", {\n subscribers: [\n \"\",\n ],\n });\n\n \n}\n\nrun();"}]}}, "/v1/topics/{topicKey}/subscribers/{externalSubscriberId}": {"get": {"operationId": "TopicsController_getTopicSubscriber", "x-speakeasy-group": "Topics.Subscribers", "summary": "Check topic subscriber", "description": "Check if a subscriber belongs to a certain topic", "parameters": [{"name": "topicKey", "required": true, "in": "path", "schema": {"type": "string"}}, {"name": "externalSubscriberId", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/TopicSubscriberDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Topics"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "TopicsController_getTopicSubscriber", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.topics.subscribers.retrieve(\"\", \"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/topics/{topicKey}/subscribers/removal": {"post": {"operationId": "TopicsController_removeSubscribers", "x-speakeasy-group": "Topics.Subscribers", "summary": "Subscribers removal", "description": "Remove subscribers from a topic", "parameters": [{"name": "topicKey", "required": true, "in": "path", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/RemoveSubscribersRequestDto"}}}}, "responses": {"204": {"description": ""}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Topics"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "TopicsController_removeSubscribers", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n await novu.topics.subscribers.delete(\"\", {\n subscribers: [\n \"\",\n ],\n });\n\n \n}\n\nrun();"}]}}, "/v1/topics/{topicKey}": {"delete": {"operationId": "TopicsController_deleteTopic", "summary": "Delete topic", "description": "Delete a topic by its topic key if it has no subscribers", "parameters": [{"name": "topicKey", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"204": {"description": "The topic has been deleted correctly"}, "404": {"description": "The topic with the key provided does not exist in the database so it can not be deleted."}, "409": {"description": "The topic you are trying to delete has subscribers assigned to it. Delete the subscribers before deleting the topic.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Topics"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "TopicsController_deleteTopic", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n await novu.topics.delete(\"\");\n\n \n}\n\nrun();"}]}, "get": {"operationId": "TopicsController_getTopic", "summary": "Get topic", "description": "Get a topic by its topic key", "parameters": [{"name": "topicKey", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/GetTopicResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Topics"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "TopicsController_getTopic", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.topics.retrieve(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}, "patch": {"operationId": "TopicsController_renameTopic", "x-speakeasy-name-override": "rename", "summary": "Rename a topic", "description": "Rename a topic by providing a new name", "parameters": [{"name": "topicKey", "required": true, "in": "path", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/RenameTopicRequestDto"}}}}, "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/RenameTopicResponseDto"}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Topics"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "TopicsController_renameTopic", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.topics.rename(\"\", {\n name: \"\",\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/tenants": {"get": {"operationId": "TenantController_listTenants", "x-speakeasy-pagination": {"type": "offsetLimit", "inputs": [{"name": "page", "in": "parameters", "type": "page"}, {"name": "limit", "in": "parameters", "type": "limit"}], "outputs": {"results": "$.data.resultArray"}}, "summary": "Get tenants", "description": "Returns a list of tenants, could paginated using the `page` and `limit` query parameter", "parameters": [{"name": "page", "required": false, "in": "query", "schema": {"type": "number"}}, {"name": "limit", "required": false, "in": "query", "schema": {"maximum": 100, "default": 10, "type": "number"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"allOf": [{"$ref": "#/components/schemas/PaginatedResponseDto"}, {"properties": {"data": {"type": "array", "items": {"$ref": "#/components/schemas/GetTenantResponseDto"}}}}]}}}}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Tenants"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "TenantController_listTenants", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.tenants.list(7685.78, 10);\n\n for await (const page of result) {\n // handle page\n }\n}\n\nrun();"}]}, "post": {"operationId": "TenantController_createTenant", "summary": "Create tenant", "description": "Create tenant under the current environment", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CreateTenantRequestDto"}}}}, "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CreateTenantResponseDto"}}}}, "201": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CreateTenantResponseDto"}}}}, "409": {"description": "A tenant with the same identifier is already exist.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Tenants"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "TenantController_createTenant", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.tenants.create({\n identifier: \"\",\n name: \"\",\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}}, "/v1/tenants/{identifier}": {"get": {"operationId": "TenantController_getTenantById", "summary": "Get tenant", "description": "Get tenant by your internal id used to identify the tenant", "parameters": [{"name": "identifier", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/GetTenantResponseDto"}}}}, "404": {"description": "The tenant with the identifier provided does not exist in the database."}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Tenants"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "TenantController_getTenantById", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.tenants.retrieve(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}, "patch": {"operationId": "TenantController_updateTenant", "summary": "Update tenant", "description": "Update tenant by your internal id used to identify the tenant", "parameters": [{"name": "identifier", "required": true, "in": "path", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UpdateTenantRequestDto"}}}}, "responses": {"200": {"description": "Ok", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UpdateTenantResponseDto"}}}}, "404": {"description": "The tenant with the identifier provided does not exist in the database."}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Tenants"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "TenantController_updateTenant", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.tenants.update(\"\", {});\n\n // Handle the result\n console.log(result)\n}\n\nrun();"}]}, "delete": {"operationId": "TenantController_removeTenant", "summary": "Delete tenant", "description": "Deletes a tenant entity from the Novu platform", "parameters": [{"name": "identifier", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"204": {"description": "The tenant has been deleted correctly"}, "404": {"description": "The tenant with the identifier provided does not exist in the database so it can not be deleted."}, "409": {"description": "The request could not be completed due to a conflict with the current state of the target resource.", "content": {"application/json": {"schema": {"type": "string", "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second"}}}}, "429": {"description": "The client has sent too many requests in a given amount of time. ", "content": {"application/json": {"schema": {"type": "string", "example": "API rate limit exceeded"}}}}, "503": {"description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", "content": {"application/json": {"schema": {"type": "string", "example": "Please wait some time, then try again."}}}}}, "tags": ["Tenants"], "security": [{"api-key": []}], "x-codeSamples": [{"lang": "typescript", "label": "TenantController_removeTenant", "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n await novu.tenants.delete(\"\");\n\n \n}\n\nrun();"}]}}}, "info": {"title": "Novu API", "description": "Novu REST API. Please see https://docs.novu.co/api-reference for more details.", "version": "1.0", "contact": {"name": "Novu Support", "url": "https://discord.gg/novu", "email": "support@novu.co"}, "termsOfService": "https://novu.co/terms", "license": {"name": "MIT", "url": "https://opensource.org/license/mit"}}, "tags": [{"name": "Events", "description": "Events represent a change in state of a subscriber. They are used to trigger workflows, and enable you to send notifications to subscribers based on their actions.", "externalDocs": {"url": "https://docs.novu.co/workflows"}}, {"name": "Subscribers", "description": "A subscriber in Novu represents someone who should receive a message. A subscriber’s profile information contains important attributes about the subscriber that will be used in messages (name, email). The subscriber object can contain other key-value pairs that can be used to further personalize your messages.", "externalDocs": {"url": "https://docs.novu.co/subscribers/subscribers"}}, {"name": "Topics", "description": "Topics are a way to group subscribers together so that they can be notified of events at once. A topic is identified by a custom key. This can be helpful for things like sending out marketing emails or notifying users of new features. Topics can also be used to send notifications to the subscribers who have been grouped together based on their interests, location, activities and much more.", "externalDocs": {"url": "https://docs.novu.co/subscribers/topics"}}, {"name": "Notification", "description": "A notification conveys information from source to recipient, triggered by a workflow acting as a message blueprint. Notifications can be individual or bundled as digest for user-friendliness.", "externalDocs": {"url": "https://docs.novu.co/getting-started/introduction"}}, {"name": "Integrations", "description": "With the help of the Integration Store, you can easily integrate your favorite delivery provider. During the runtime of the API, the Integrations Store is responsible for storing the configurations of all the providers.", "externalDocs": {"url": "https://docs.novu.co/channels-and-providers/integration-store"}}, {"name": "Layouts", "description": "Novu allows the creation of layouts - a specific HTML design or structure to wrap content of email notifications. Layouts can be manipulated and assigned to new or existing workflows within the Novu platform, allowing users to create, manage, and assign these layouts to workflows, so they can be reused to structure the appearance of notifications sent through the platform.", "externalDocs": {"url": "https://docs.novu.co/content-creation-design/layouts"}}, {"name": "Workflows", "description": "All notifications are sent via a workflow. Each workflow acts as a container for the logic and blueprint that are associated with a type of notification in your system.", "externalDocs": {"url": "https://docs.novu.co/workflows"}}, {"name": "Notification Templates", "description": "Deprecated. Use Workflows (/workflows) instead, which provide the same functionality under a new name."}, {"name": "Workflow groups", "description": "Workflow groups are used to organize workflows into logical groups."}, {"name": "Changes", "description": "Changes represent a change in state of an environment. They are analagous to a pending pull request in git, enabling you to test changes before they are applied to your environment and atomically apply them when you are ready.", "externalDocs": {"url": "https://docs.novu.co/platform/environments#promoting-pending-changes-to-production"}}, {"name": "Environments", "description": "Novu uses the concept of environments to ensure logical separation of your data and configuration. This means that subscribers, and preferences created in one environment are never accessible to another.", "externalDocs": {"url": "https://docs.novu.co/platform/environments"}}, {"name": "Inbound Parse", "description": "Inbound Webhook is a feature that allows processing of incoming emails for a domain or subdomain. The feature parses the contents of the email and POSTs the information to a specified URL in a multipart/form-data format.", "externalDocs": {"url": "https://docs.novu.co/platform/inbound-parse-webhook"}}, {"name": "Feeds", "description": "Novu provides a notification activity feed that monitors every outgoing message associated with its relevant metadata. This can be used to monitor activity and discover potential issues with a specific provider or a channel type.", "externalDocs": {"url": "https://docs.novu.co/activity-feed"}}, {"name": "Tenants", "description": "A tenant represents a group of users. As a developer, when your apps have organizations, they are referred to as tenants. Tenants in Novu provides the ability to tailor specific notification experiences to users of different groups or organizations.", "externalDocs": {"url": "https://docs.novu.co/tenants"}}, {"name": "Messages", "description": "A message in Novu represents a notification delivered to a recipient on a particular channel. Messages contain information about the request that triggered its delivery, a view of the data sent to the recipient, and a timeline of its lifecycle events. Learn more about messages.", "externalDocs": {"url": "https://docs.novu.co/workflows/messages"}}, {"name": "Organizations", "description": "An organization serves as a separate entity within your Novu account. Each organization you create has its own separate integration store, workflows, subscribers, and API keys. This separation of resources allows you to manage multi-tenant environments and separate domains within a single account.", "externalDocs": {"url": "https://docs.novu.co/platform/organizations"}}, {"name": "Execution Details", "description": "Execution details are used to track the execution of a workflow. They provided detailed information on the execution of a workflow, including the status of each step, the input and output of each step, and the overall status of the execution.", "externalDocs": {"url": "https://docs.novu.co/activity-feed"}}], "servers": [{"url": "https://api.novu.co"}, {"url": "https://eu.api.novu.co"}], "components": {"securitySchemes": {"api-key": {"type": "apiKey", "in": "header", "name": "Authorization", "description": "API key authentication. Allowed headers-- \"Authorization: ApiKey \"."}}, "schemas": {"DataWrapperDto": {"type": "object", "properties": {"data": {"type": "object"}}, "required": ["data"]}, "OrganizationBrandingResponseDto": {"type": "object", "properties": {"direction": {"enum": ["ltr", "trl"], "type": "string"}, "logo": {"type": "string"}, "color": {"type": "string"}, "fontColor": {"type": "string"}, "contentBackground": {"type": "string"}, "fontFamily": {"type": "string"}}, "required": ["logo", "color", "fontColor", "contentBackground"]}, "IPartnerConfigurationResponseDto": {"type": "object", "properties": {"projectIds": {"type": "array", "items": {"type": "string"}}, "accessToken": {"type": "string"}, "configurationId": {"type": "string"}, "teamId": {"type": "string"}, "partnerType": {"type": "string", "enum": ["vercel"], "description": "Partner Type Enum"}}, "required": ["accessToken", "configurationId", "partnerType"]}, "OrganizationResponseDto": {"type": "object", "properties": {"name": {"type": "string"}, "logo": {"type": "string"}, "branding": {"$ref": "#/components/schemas/OrganizationBrandingResponseDto"}, "partnerConfigurations": {"type": "array", "items": {"$ref": "#/components/schemas/IPartnerConfigurationResponseDto"}}}, "required": ["name", "branding"]}, "CreateOrganizationDto": {"type": "object", "properties": {"name": {"type": "string"}, "logo": {"type": "string"}, "jobTitle": {"enum": ["engineer", "engineering_manager", "architect", "product_manager", "designer", "cxo_founder", "marketing_manager", "other"], "type": "string"}, "domain": {"type": "string"}, "productUseCases": {"type": "object"}}, "required": ["name"]}, "MemberUserDto": {"type": "object", "properties": {"_id": {"type": "string"}, "firstName": {"type": "string"}, "lastName": {"type": "string"}, "email": {"type": "string"}}, "required": ["_id", "firstName", "lastName", "email"]}, "MemberInviteDTO": {"type": "object", "properties": {"email": {"type": "string"}, "token": {"type": "string"}, "invitationDate": {"format": "date-time", "type": "string"}, "answerDate": {"format": "date-time", "type": "string"}, "_inviterId": {"type": "string"}}, "required": ["email", "token", "invitationDate", "_inviterId"]}, "MemberResponseDto": {"type": "object", "properties": {"_id": {"type": "string"}, "_userId": {"type": "string"}, "user": {"$ref": "#/components/schemas/MemberUserDto"}, "roles": {"enum": ["admin", "member"], "type": "string"}, "invite": {"$ref": "#/components/schemas/MemberInviteDTO"}, "memberStatus": {"enum": ["new", "active", "invited"], "type": "string"}, "_organizationId": {"type": "string"}}, "required": ["_id", "_userId", "_organizationId"]}, "UpdateBrandingDetailsDto": {"type": "object", "properties": {"logo": {"type": "string"}, "color": {"type": "string"}, "fontColor": {"type": "string"}, "contentBackground": {"type": "string"}, "fontFamily": {"type": "string"}}, "required": ["logo", "color", "fontColor", "contentBackground"]}, "RenameOrganizationDto": {"type": "object", "properties": {"name": {"type": "string"}}, "required": ["name"]}, "EnvironmentResponseDto": {"type": "object", "properties": {"_id": {"type": "string"}, "name": {"type": "string"}, "_organizationId": {"type": "string"}, "identifier": {"type": "string"}, "apiKeys": {"type": "array", "items": {"type": "object"}}, "_parentId": {"type": "string"}}, "required": ["name", "_organizationId", "identifier", "_parentId"]}, "ApiKey": {"type": "object", "properties": {"key": {"type": "string"}, "_userId": {"type": "string"}}, "required": ["key", "_userId"]}, "ExecutionDetailsResponseDto": {"type": "object", "properties": {"_id": {"type": "string"}, "_organizationId": {"type": "string"}, "_jobId": {"type": "string"}, "_environmentId": {"type": "string"}, "_notificationId": {"type": "string"}, "_notificationTemplateId": {"type": "string"}, "_subscriberId": {"type": "string"}, "_messageId": {"type": "string"}, "providerId": {"type": "string"}, "transactionId": {"type": "string"}, "channel": {"type": "string", "enum": ["in_app", "email", "sms", "chat", "push", "digest", "trigger", "delay", "custom"]}, "detail": {"type": "string"}, "source": {"type": "string", "enum": ["Credentials", "Internal", "Payload", "Webhook"]}, "status": {"type": "string", "enum": ["Success", "Warning", "Failed", "Pending", "Queued", "ReadConfirmation"]}, "isTest": {"type": "boolean"}, "isRetry": {"type": "boolean"}, "createdAt": {"type": "string"}}, "required": ["_organizationId", "_jobId", "_environmentId", "_notificationId", "_notificationTemplateId", "_subscriberId", "transactionId", "channel", "detail", "source", "status", "isTest", "isRetry"]}, "NotificationGroup": {"type": "object", "properties": {"_id": {"type": "string"}, "name": {"type": "string"}, "_environmentId": {"type": "string"}, "_organizationId": {"type": "string"}, "_parentId": {"type": "string"}}, "required": ["name", "_environmentId", "_organizationId"]}, "PreferenceChannels": {"type": "object", "properties": {"email": {"type": "boolean"}, "sms": {"type": "boolean"}, "in_app": {"type": "boolean"}, "chat": {"type": "boolean"}, "push": {"type": "boolean"}}}, "DigestRegularMetadata": {"type": "object", "properties": {"amount": {"type": "number"}, "unit": {"type": "string", "enum": ["seconds", "minutes", "hours", "days", "weeks", "months"]}, "digestKey": {"type": "string"}, "type": {"type": "string", "enum": ["regular", "backoff"]}, "backoff": {"type": "boolean"}, "backoffAmount": {"type": "number"}, "backoffUnit": {"type": "string", "enum": ["seconds", "minutes", "hours", "days", "weeks", "months"]}, "updateMode": {"type": "boolean"}}, "required": ["type"]}, "TimedConfig": {"type": "object", "properties": {"atTime": {"type": "string"}, "weekDays": {"type": "array", "items": {"type": "string", "enum": ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"]}}, "monthDays": {"type": "array", "items": {"type": "string"}}, "ordinal": {"type": "string", "enum": ["1", "2", "3", "4", "5", "last"]}, "ordinalValue": {"type": "string", "enum": ["day", "weekday", "weekend", "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"]}, "monthlyType": {"type": "string", "enum": ["each", "on"]}}}, "DigestTimedMetadata": {"type": "object", "properties": {"amount": {"type": "number"}, "unit": {"type": "string", "enum": ["seconds", "minutes", "hours", "days", "weeks", "months"]}, "digestKey": {"type": "string"}, "type": {"type": "string", "enum": ["timed"]}, "timed": {"$ref": "#/components/schemas/TimedConfig"}}, "required": ["type"]}, "DelayRegularMetadata": {"type": "object", "properties": {"amount": {"type": "number"}, "unit": {"type": "string", "enum": ["seconds", "minutes", "hours", "days", "weeks", "months"]}, "type": {"type": "string", "enum": ["regular"]}}, "required": ["type"]}, "DelayScheduledMetadata": {"type": "object", "properties": {"type": {"type": "string", "enum": ["scheduled"]}, "delayPath": {"type": "string"}}, "required": ["type", "delayPath"]}, "MessageTemplate": {"type": "object", "properties": {}}, "FieldFilterPart": {"type": "object", "properties": {"field": {"type": "string"}, "value": {"type": "string"}, "operator": {"type": "string", "enum": ["LARGER", "SMALLER", "LARGER_EQUAL", "SMALLER_EQUAL", "EQUAL", "NOT_EQUAL", "ALL_IN", "ANY_IN", "NOT_IN", "BETWEEN", "NOT_BETWEEN", "LIKE", "NOT_LIKE", "IN"]}, "on": {"type": "string", "enum": ["subscriber", "payload"]}}, "required": ["field", "value", "operator", "on"]}, "StepFilter": {"type": "object", "properties": {"isNegated": {"type": "boolean"}, "type": {"type": "string", "enum": ["BOOLEAN", "TEXT", "DATE", "NUMBER", "STATEMENT", "LIST", "MULTI_LIST", "GROUP"]}, "value": {"type": "string", "enum": ["AND", "OR"]}, "children": {"type": "array", "items": {"$ref": "#/components/schemas/FieldFilterPart"}}}, "required": ["isNegated", "type", "value", "children"]}, "NotificationStepVariant": {"type": "object", "properties": {"_id": {"type": "string"}, "uuid": {"type": "string"}, "name": {"type": "string"}, "_templateId": {"type": "string"}, "active": {"type": "boolean"}, "shouldStopOnFail": {"type": "boolean"}, "template": {"$ref": "#/components/schemas/MessageTemplate"}, "filters": {"type": "array", "items": {"$ref": "#/components/schemas/StepFilter"}}, "_parentId": {"type": "object"}, "metadata": {"oneOf": [{"$ref": "#/components/schemas/DigestRegularMetadata"}, {"$ref": "#/components/schemas/DigestTimedMetadata"}, {"$ref": "#/components/schemas/DelayRegularMetadata"}, {"$ref": "#/components/schemas/DelayScheduledMetadata"}]}, "replyCallback": {"type": "object"}}}, "NotificationStep": {"type": "object", "properties": {"_id": {"type": "string"}, "uuid": {"type": "string"}, "name": {"type": "string"}, "_templateId": {"type": "string"}, "active": {"type": "boolean"}, "shouldStopOnFail": {"type": "boolean"}, "template": {"$ref": "#/components/schemas/MessageTemplate"}, "filters": {"type": "array", "items": {"$ref": "#/components/schemas/StepFilter"}}, "_parentId": {"type": "object"}, "metadata": {"oneOf": [{"$ref": "#/components/schemas/DigestRegularMetadata"}, {"$ref": "#/components/schemas/DigestTimedMetadata"}, {"$ref": "#/components/schemas/DelayRegularMetadata"}, {"$ref": "#/components/schemas/DelayScheduledMetadata"}]}, "replyCallback": {"type": "object"}, "variants": {"$ref": "#/components/schemas/NotificationStepVariant"}}}, "NotificationTriggerVariable": {"type": "object", "properties": {"name": {"type": "string"}}, "required": ["name"]}, "NotificationTrigger": {"type": "object", "properties": {"type": {"type": "string", "enum": ["event"]}, "identifier": {"type": "string"}, "variables": {"type": "array", "items": {"$ref": "#/components/schemas/NotificationTriggerVariable"}}, "subscriberVariables": {"type": "array", "items": {"$ref": "#/components/schemas/NotificationTriggerVariable"}}}, "required": ["type", "identifier", "variables"]}, "WorkflowResponse": {"type": "object", "properties": {"_id": {"type": "string"}, "name": {"type": "string"}, "description": {"type": "string"}, "active": {"type": "boolean"}, "draft": {"type": "boolean"}, "preferenceSettings": {"$ref": "#/components/schemas/PreferenceChannels"}, "critical": {"type": "boolean"}, "tags": {"type": "array", "items": {"type": "string"}}, "steps": {"type": "array", "items": {"$ref": "#/components/schemas/NotificationStep"}}, "_organizationId": {"type": "string"}, "_creatorId": {"type": "string"}, "_environmentId": {"type": "string"}, "triggers": {"type": "array", "items": {"$ref": "#/components/schemas/NotificationTrigger"}}, "_notificationGroupId": {"type": "string"}, "_parentId": {"type": "string"}, "deleted": {"type": "boolean"}, "deletedAt": {"type": "string"}, "deletedBy": {"type": "string"}, "notificationGroup": {"$ref": "#/components/schemas/NotificationGroup"}, "data": {"type": "object"}, "workflowIntegrationStatus": {"type": "object"}}, "required": ["name", "description", "active", "draft", "preferenceSettings", "critical", "tags", "steps", "_organizationId", "_creatorId", "_environmentId", "triggers", "_notificationGroupId", "deleted", "deletedAt", "deletedBy"]}, "WorkflowsResponseDto": {"type": "object", "properties": {"totalCount": {"type": "number"}, "data": {"type": "array", "items": {"$ref": "#/components/schemas/WorkflowResponse"}}, "pageSize": {"type": "number"}, "page": {"type": "number"}}, "required": ["totalCount", "data", "pageSize", "page"]}, "UpdateWorkflowRequestDto": {"type": "object", "properties": {"name": {"type": "string"}, "tags": {"type": "array", "items": {"type": "string"}}, "description": {"type": "string", "maxLength": 300}, "identifier": {"type": "string"}, "steps": {"type": "array", "items": {"$ref": "#/components/schemas/NotificationStep"}}, "notificationGroupId": {"type": "string"}, "critical": {"type": "boolean"}, "preferenceSettings": {"$ref": "#/components/schemas/PreferenceChannels"}, "data": {"type": "object"}}, "required": ["name", "notificationGroupId"]}, "DataBooleanDto": {"type": "object", "properties": {"data": {"type": "boolean"}}, "required": ["data"]}, "VariablesResponseDto": {"type": "object", "properties": {"translations": {"type": "object"}, "system": {"type": "object"}}, "required": ["translations", "system"]}, "CreateWorkflowRequestDto": {"type": "object", "properties": {"name": {"type": "string"}, "notificationGroupId": {"type": "string"}, "notificationGroup": {"type": "object"}, "tags": {"type": "array", "items": {"type": "string"}}, "description": {"type": "string", "maxLength": 1000}, "steps": {"type": "array", "items": {"$ref": "#/components/schemas/NotificationStep"}}, "active": {"type": "boolean"}, "draft": {"type": "boolean", "deprecated": true}, "critical": {"type": "boolean"}, "preferenceSettings": {"$ref": "#/components/schemas/PreferenceChannels"}, "blueprintId": {"type": "string"}, "data": {"type": "object"}}, "required": ["name", "notificationGroupId", "steps"]}, "ChangeWorkflowStatusRequestDto": {"type": "object", "properties": {"active": {"type": "boolean"}}, "required": ["active"]}, "TriggerEventResponseDto": {"type": "object", "properties": {"acknowledged": {"type": "boolean", "description": "If trigger was acknowledged or not"}, "status": {"enum": ["error", "trigger_not_active", "no_workflow_active_steps_defined", "no_workflow_steps_defined", "processed", "subscriber_id_missing", "no_tenant_found"], "type": "string", "description": "Status for trigger"}, "error": {"description": "In case of an error, this field will contain the error message", "type": "array", "items": {"type": "string"}}, "transactionId": {"type": "string", "description": "Transaction id for trigger"}}, "required": ["acknowledged", "status"]}, "TopicPayloadDto": {"type": "object", "properties": {"topicKey": {"type": "string", "example": "topic_key"}, "type": {"enum": ["Subscriber", "Topic"], "type": "string", "example": "Topic"}}, "required": ["topicKey", "type"]}, "TenantPayloadDto": {"type": "object", "properties": {"identifier": {"type": "string"}, "name": {"type": "string"}, "data": {"type": "object"}}}, "ChannelCredentialsDto": {"type": "object", "properties": {"webhookUrl": {"type": "string"}, "deviceTokens": {"type": "array", "items": {"type": "string"}}}}, "SubscriberChannelDto": {"type": "object", "properties": {"integrationIdentifier": {"type": "string"}, "providerId": {"type": "object"}, "credentials": {"$ref": "#/components/schemas/ChannelCredentialsDto"}}, "required": ["providerId", "credentials"]}, "SubscriberPayloadDto": {"type": "object", "properties": {"subscriberId": {"type": "string", "description": "The internal identifier you used to create this subscriber, usually correlates to the id the user in your systems"}, "email": {"type": "string"}, "firstName": {"type": "string"}, "lastName": {"type": "string"}, "phone": {"type": "string"}, "avatar": {"type": "string", "description": "An http url to the profile image of your subscriber"}, "locale": {"type": "string"}, "data": {"type": "object"}, "channels": {"type": "array", "items": {"$ref": "#/components/schemas/SubscriberChannelDto"}}}, "required": ["subscriberId"]}, "TriggerEventRequestDto": {"type": "object", "properties": {"name": {"type": "string", "description": "The trigger identifier of the workflow you wish to send. This identifier can be found on the workflow page.", "example": "workflow_identifier"}, "payload": {"type": "object", "description": "The payload object is used to pass additional custom information that could be used to render the workflow, or perform routing rules based on it. \n This data will also be available when fetching the notifications feed from the API to display certain parts of the UI.", "example": {"comment_id": "string", "post": {"text": "string"}}}, "overrides": {"type": "object", "description": "This could be used to override provider specific configurations", "example": {"fcm": {"data": {"key": "value"}}}}, "to": {"type": "array", "description": "The recipients list of people who will receive the notification.", "items": {"oneOf": [{"$ref": "#/components/schemas/SubscriberPayloadDto"}, {"type": "string", "description": "Unique identifier of a subscriber in your systems", "example": "SUBSCRIBER_ID"}, {"$ref": "#/components/schemas/TopicPayloadDto"}]}}, "transactionId": {"type": "string", "description": "A unique identifier for this transaction, we will generated a UUID if not provided."}, "actor": {"description": "It is used to display the Avatar of the provided actor's subscriber id or actor object.\n If a new actor object is provided, we will create a new subscriber in our system\n ", "oneOf": [{"type": "string", "description": "Unique identifier of a subscriber in your systems"}, {"$ref": "#/components/schemas/SubscriberPayloadDto"}]}, "tenant": {"description": "It is used to specify a tenant context during trigger event.\n Existing tenants will be updated with the provided details.\n ", "oneOf": [{"type": "string", "description": "Unique identifier of a tenant in your system"}, {"$ref": "#/components/schemas/TenantPayloadDto"}]}}, "required": ["name", "to"]}, "BulkTriggerEventDto": {"type": "object", "properties": {"events": {"type": "array", "items": {"$ref": "#/components/schemas/TriggerEventRequestDto"}}}, "required": ["events"]}, "TriggerEventToAllRequestDto": {"type": "object", "properties": {"name": {"type": "string", "description": "The trigger identifier associated for the template you wish to send. This identifier can be found on the template page."}, "payload": {"type": "object", "description": "The payload object is used to pass additional custom information that could be used to render the template, or perform routing rules based on it. \n This data will also be available when fetching the notifications feed from the API to display certain parts of the UI.", "example": {"comment_id": "string", "post": {"text": "string"}}}, "overrides": {"type": "object", "description": "This could be used to override provider specific configurations", "example": {"fcm": {"data": {"key": "value"}}}}, "transactionId": {"type": "string", "description": "A unique identifier for this transaction, we will generated a UUID if not provided."}, "actor": {"description": "It is used to display the Avatar of the provided actor's subscriber id or actor object.\n If a new actor object is provided, we will create a new subscriber in our system\n ", "oneOf": [{"type": "string", "description": "Unique identifier of a subscriber in your systems"}, {"$ref": "#/components/schemas/SubscriberPayloadDto"}]}, "tenant": {"description": "It is used to specify a tenant context during trigger event.\n If a new tenant object is provided, we will create a new tenant.\n ", "oneOf": [{"type": "string", "description": "Unique identifier of a tenant in your system"}, {"$ref": "#/components/schemas/TenantPayloadDto"}]}}, "required": ["name", "payload"]}, "ActivityNotificationSubscriberResponseDto": {"type": "object", "properties": {"firstName": {"type": "string"}, "_id": {"type": "string"}, "lastName": {"type": "string"}, "email": {"type": "string"}, "phone": {"type": "string"}}, "required": ["_id"]}, "ActivityNotificationTemplateResponseDto": {"type": "object", "properties": {"_id": {"type": "string"}, "name": {"type": "string"}, "triggers": {"type": "array", "items": {"$ref": "#/components/schemas/NotificationTrigger"}}}, "required": ["name", "triggers"]}, "ActivityNotificationExecutionDetailResponseDto": {"type": "object", "properties": {"_id": {"type": "string"}, "_jobId": {"type": "string"}, "status": {"enum": ["Success", "Warning", "Failed", "Pending", "Queued", "ReadConfirmation"], "type": "string"}, "detail": {"type": "string"}, "isRetry": {"type": "boolean"}, "isTest": {"type": "boolean"}, "providerId": {"type": "object"}, "raw": {"type": "string"}, "source": {"enum": ["Credentials", "Internal", "Payload", "Webhook"], "type": "string"}}, "required": ["_id", "_jobId", "status", "detail", "isRetry", "isTest", "providerId", "source"]}, "MessageTemplateDto": {"type": "object", "properties": {}}, "ActivityNotificationStepResponseDto": {"type": "object", "properties": {"_id": {"type": "string"}, "active": {"type": "boolean"}, "filters": {"$ref": "#/components/schemas/StepFilter"}, "template": {"$ref": "#/components/schemas/MessageTemplateDto"}}, "required": ["_id", "active", "filters"]}, "ActivityNotificationJobResponseDto": {"type": "object", "properties": {"_id": {"type": "string"}, "type": {"type": "string"}, "digest": {"type": "object"}, "executionDetails": {"type": "array", "items": {"$ref": "#/components/schemas/ActivityNotificationExecutionDetailResponseDto"}}, "step": {"$ref": "#/components/schemas/ActivityNotificationStepResponseDto"}, "payload": {"type": "object"}, "providerId": {"type": "object"}, "status": {"type": "string"}}, "required": ["_id", "type", "executionDetails", "step", "providerId", "status"]}, "ActivityNotificationResponseDto": {"type": "object", "properties": {"_id": {"type": "string"}, "_environmentId": {"type": "string"}, "_organizationId": {"type": "string"}, "transactionId": {"type": "string"}, "createdAt": {"type": "string"}, "channels": {"type": "string", "items": {"type": "string", "enum": ["in_app", "email", "sms", "chat", "push", "digest", "trigger", "delay", "custom"]}, "enum": ["in_app", "email", "sms", "chat", "push", "digest", "trigger", "delay", "custom"]}, "subscriber": {"$ref": "#/components/schemas/ActivityNotificationSubscriberResponseDto"}, "template": {"$ref": "#/components/schemas/ActivityNotificationTemplateResponseDto"}, "jobs": {"type": "array", "items": {"$ref": "#/components/schemas/ActivityNotificationJobResponseDto"}}}, "required": ["_environmentId", "_organizationId", "transactionId"]}, "ActivitiesResponseDto": {"type": "object", "properties": {"hasMore": {"type": "boolean"}, "data": {"type": "array", "items": {"$ref": "#/components/schemas/ActivityNotificationResponseDto"}}, "pageSize": {"type": "number"}, "page": {"type": "number"}}, "required": ["hasMore", "data", "pageSize", "page"]}, "ActivityStatsResponseDto": {"type": "object", "properties": {"weeklySent": {"type": "number"}, "monthlySent": {"type": "number"}}, "required": ["weeklySent", "monthlySent"]}, "ActivityGraphStatesResponse": {"type": "object", "properties": {"_id": {"type": "string"}, "count": {"type": "number"}, "templates": {"type": "array", "items": {"type": "string"}}, "channels": {"type": "array", "items": {"type": "string", "enum": ["in_app", "email", "sms", "chat", "push"]}}}, "required": ["_id", "count", "templates", "channels"]}, "NotificationGroupResponseDto": {"type": "object", "properties": {"_id": {"type": "string"}, "name": {"type": "string"}, "_environmentId": {"type": "string"}, "_organizationId": {"type": "string"}, "_parentId": {"type": "string"}}, "required": ["name", "_environmentId", "_organizationId"]}, "CreateNotificationGroupRequestDto": {"type": "object", "properties": {"name": {"type": "string"}}, "required": ["name"]}, "DeleteNotificationGroupResponseDto": {"type": "object", "properties": {"acknowledged": {"type": "boolean", "description": "A boolean stating the success of the action"}, "status": {"type": "string", "description": "The status enum for the performed action", "enum": ["deleted"]}}, "required": ["acknowledged", "status"]}, "CredentialsDto": {"type": "object", "properties": {"apiKey": {"type": "string"}, "user": {"type": "string"}, "secretKey": {"type": "string"}, "domain": {"type": "string"}, "password": {"type": "string"}, "host": {"type": "string"}, "port": {"type": "string"}, "secure": {"type": "boolean"}, "region": {"type": "string"}, "accountSid": {"type": "string"}, "messageProfileId": {"type": "string"}, "token": {"type": "string"}, "from": {"type": "string"}, "senderName": {"type": "string"}, "projectName": {"type": "string"}, "applicationId": {"type": "string"}, "clientId": {"type": "string"}, "requireTls": {"type": "boolean"}, "ignoreTls": {"type": "boolean"}, "tlsOptions": {"type": "object"}, "baseUrl": {"type": "string"}, "webhookUrl": {"type": "string"}, "redirectUrl": {"type": "string"}, "hmac": {"type": "boolean"}, "serviceAccount": {"type": "string"}, "ipPoolName": {"type": "string"}, "apiKeyRequestHeader": {"type": "string"}, "secretKeyRequestHeader": {"type": "string"}, "idPath": {"type": "string"}, "datePath": {"type": "string"}, "apiToken": {"type": "string"}, "authenticateByToken": {"type": "boolean"}, "authenticationTokenKey": {"type": "string"}, "instanceId": {"type": "string"}, "alertUid": {"type": "string"}, "title": {"type": "string"}, "imageUrl": {"type": "string"}, "state": {"type": "string"}, "externalLink": {"type": "string"}, "channelId": {"type": "string"}, "phoneNumberIdentification": {"type": "string"}}}, "IntegrationResponseDto": {"type": "object", "properties": {"_id": {"type": "string"}, "_environmentId": {"type": "string"}, "_organizationId": {"type": "string"}, "name": {"type": "string"}, "identifier": {"type": "string"}, "providerId": {"type": "string"}, "channel": {"enum": ["in_app", "email", "sms", "chat", "push"], "type": "string"}, "credentials": {"$ref": "#/components/schemas/CredentialsDto"}, "active": {"type": "boolean"}, "deleted": {"type": "boolean"}, "deletedAt": {"type": "string"}, "deletedBy": {"type": "string"}, "primary": {"type": "boolean"}, "conditions": {"type": "array", "items": {"$ref": "#/components/schemas/StepFilter"}}}, "required": ["_environmentId", "_organizationId", "name", "identifier", "providerId", "channel", "credentials", "active", "deleted", "deletedAt", "deletedBy", "primary"]}, "CreateIntegrationRequestDto": {"type": "object", "properties": {"name": {"type": "string"}, "identifier": {"type": "string"}, "_environmentId": {"type": "string"}, "providerId": {"type": "string"}, "channel": {"enum": ["in_app", "email", "sms", "chat", "push"], "type": "string"}, "credentials": {"$ref": "#/components/schemas/CredentialsDto"}, "active": {"type": "boolean", "description": "If the integration is active the validation on the credentials field will run"}, "check": {"type": "boolean"}, "conditions": {"type": "array", "items": {"$ref": "#/components/schemas/StepFilter"}}}, "required": ["providerId", "channel"]}, "UpdateIntegrationRequestDto": {"type": "object", "properties": {"name": {"type": "string"}, "identifier": {"type": "string"}, "_environmentId": {"type": "string"}, "active": {"type": "boolean", "description": "If the integration is active the validation on the credentials field will run"}, "credentials": {"$ref": "#/components/schemas/CredentialsDto"}, "check": {"type": "boolean"}, "conditions": {"type": "array", "items": {"$ref": "#/components/schemas/StepFilter"}}}}, "ChangeResponseDto": {"type": "object", "properties": {"_id": {"type": "string"}, "_creatorId": {"type": "string"}, "_environmentId": {"type": "string"}, "_organizationId": {"type": "string"}, "_entityId": {"type": "string"}, "enabled": {"type": "boolean"}, "type": {"enum": ["Feed", "MessageTemplate", "Layout", "DefaultLayout", "NotificationTemplate", "NotificationGroup", "TranslationGroup", "Translation"], "type": "string"}, "change": {"type": "object"}, "createdAt": {"type": "string"}, "_parentId": {"type": "string"}}, "required": ["_creatorId", "_environmentId", "_organizationId", "_entityId", "enabled", "type", "change", "createdAt"]}, "ChangesResponseDto": {"type": "object", "properties": {"totalCount": {"type": "number"}, "data": {"type": "array", "items": {"$ref": "#/components/schemas/ChangeResponseDto"}}, "pageSize": {"type": "number"}, "page": {"type": "number"}}, "required": ["totalCount", "data", "pageSize", "page"]}, "DataNumberDto": {"type": "object", "properties": {"data": {"type": "number"}}, "required": ["data"]}, "BulkApplyChangeDto": {"type": "object", "properties": {"changeIds": {"type": "array", "items": {"type": "string"}}}, "required": ["changeIds"]}, "PaginatedResponseDto": {"type": "object", "properties": {"page": {"type": "number", "description": "The current page of the paginated response"}, "hasMore": {"type": "boolean", "description": "Does the list have more items to fetch"}, "pageSize": {"type": "number", "description": "Number of items on each page"}, "data": {"description": "The list of items matching the query", "type": "array", "items": {"type": "object"}}}, "required": ["page", "hasMore", "pageSize", "data"]}, "ChannelCredentials": {"type": "object", "properties": {"webhookUrl": {"type": "string", "description": "Webhook url used by chat app integrations. The webhook should be obtained from the chat app provider."}, "channel": {"type": "string", "description": "Channel specification for Mattermost chat notifications"}, "deviceTokens": {"description": "Contains an array of the subscriber device tokens for a given provider. Used on Push integrations", "type": "array", "items": {"type": "string"}}, "alertUid": {"type": "string", "description": "alert_uid for grafana on-call webhook payload"}, "title": {"type": "string", "description": "title to be used with grafana on call webhook"}, "imageUrl": {"type": "string", "description": "image_url property fo grafana on call webhook"}, "state": {"type": "string", "description": "state property fo grafana on call webhook"}, "externalUrl": {"type": "string", "description": "link_to_upstream_details property fo grafana on call webhook"}}, "required": ["webhookUrl"]}, "ChannelSettings": {"type": "object", "properties": {"providerId": {"type": "string", "enum": ["slack", "discord", "msteams", "mattermost", "ryver", "zulip", "grafana-on-call", "getstream", "rocket-chat", "whatsapp-business", "fcm", "apns", "expo", "one-signal", "pushpad", "push-webhook", "pusher-beams"], "description": "The provider identifier for the credentials"}, "integrationIdentifier": {"type": "string", "description": "The integration identifier"}, "credentials": {"description": "Credentials payload for the specified provider", "allOf": [{"$ref": "#/components/schemas/ChannelCredentials"}]}, "_integrationId": {"type": "string", "description": "Id of the integration that is used for this channel"}}, "required": ["providerId", "credentials", "_integrationId"]}, "SubscriberResponseDto": {"type": "object", "properties": {"_id": {"type": "string", "description": "The internal id novu generated for your subscriber, this is not the subscriberId matching your query. See `subscriberId` for that"}, "firstName": {"type": "string"}, "lastName": {"type": "string"}, "email": {"type": "string"}, "phone": {"type": "string"}, "avatar": {"type": "string"}, "locale": {"type": "string"}, "subscriberId": {"type": "string", "description": "The internal identifier you used to create this subscriber, usually correlates to the id the user in your systems"}, "channels": {"description": "Channels settings for subscriber", "type": "array", "items": {"$ref": "#/components/schemas/ChannelSettings"}}, "isOnline": {"type": "boolean"}, "lastOnlineAt": {"type": "string"}, "_organizationId": {"type": "string"}, "_environmentId": {"type": "string"}, "deleted": {"type": "boolean"}, "createdAt": {"type": "string"}, "updatedAt": {"type": "string"}, "__v": {"type": "number"}}, "required": ["subscriberId", "_organizationId", "_environmentId", "deleted", "createdAt", "updatedAt"]}, "CreateSubscriberRequestDto": {"type": "object", "properties": {"subscriberId": {"type": "string", "description": "The internal identifier you used to create this subscriber, usually correlates to the id the user in your systems"}, "email": {"type": "string"}, "firstName": {"type": "string"}, "lastName": {"type": "string"}, "phone": {"type": "string"}, "avatar": {"type": "string", "description": "An http url to the profile image of your subscriber"}, "locale": {"type": "string"}, "data": {"type": "object"}, "channels": {"type": "array", "items": {"$ref": "#/components/schemas/SubscriberChannelDto"}}}, "required": ["subscriberId"]}, "BulkSubscriberCreateDto": {"type": "object", "properties": {"subscribers": {"type": "array", "items": {"$ref": "#/components/schemas/CreateSubscriberRequestDto"}}}, "required": ["subscribers"]}, "UpdateSubscriberRequestDto": {"type": "object", "properties": {"email": {"type": "string"}, "firstName": {"type": "string"}, "lastName": {"type": "string"}, "phone": {"type": "string"}, "avatar": {"type": "string"}, "locale": {"type": "string"}, "data": {"type": "object"}}}, "UpdateSubscriberChannelRequestDto": {"type": "object", "properties": {"providerId": {"type": "string", "enum": ["slack", "discord", "msteams", "mattermost", "ryver", "zulip", "grafana-on-call", "getstream", "rocket-chat", "whatsapp-business", "fcm", "apns", "expo", "one-signal", "pushpad", "push-webhook", "pusher-beams"], "description": "The provider identifier for the credentials"}, "integrationIdentifier": {"type": "string", "description": "The integration identifier"}, "credentials": {"description": "Credentials payload for the specified provider", "allOf": [{"$ref": "#/components/schemas/ChannelCredentials"}]}}, "required": ["providerId", "credentials"]}, "UpdateSubscriberOnlineFlagRequestDto": {"type": "object", "properties": {"isOnline": {"type": "boolean"}}, "required": ["isOnline"]}, "DeleteSubscriberResponseDto": {"type": "object", "properties": {"acknowledged": {"type": "boolean", "description": "A boolean stating the success of the action"}, "status": {"type": "string", "description": "The status enum for the performed action", "enum": ["deleted"]}}, "required": ["acknowledged", "status"]}, "TemplateResponse": {"type": "object", "properties": {"_id": {"type": "string", "description": "Unique identifier of the workflow"}, "name": {"type": "string", "description": "Name of the workflow"}, "critical": {"type": "boolean", "description": "Critical templates will always be delivered to the end user and should be hidden from the subscriber preferences screen"}, "triggers": {"description": "Triggers are the events that will trigger the workflow.", "type": "array", "items": {"type": "string"}}}, "required": ["_id", "name", "critical", "triggers"]}, "Preference": {"type": "object", "properties": {"enabled": {"type": "boolean", "description": "Sets if the workflow is fully enabled for all channels or not for the subscriber."}, "channels": {"description": "Subscriber preferences for the different channels regarding this workflow", "allOf": [{"$ref": "#/components/schemas/PreferenceChannels"}]}}, "required": ["enabled", "channels"]}, "UpdateSubscriberPreferenceResponseDto": {"type": "object", "properties": {"template": {"description": "The workflow information and if it is critical or not", "allOf": [{"$ref": "#/components/schemas/TemplateResponse"}]}, "preference": {"description": "The preferences of the subscriber regarding the related workflow", "allOf": [{"$ref": "#/components/schemas/Preference"}]}}, "required": ["template", "preference"]}, "GetSubscriberPreferencesResponseDto": {"type": "object", "properties": {"template": {"description": "The workflow information and if it is critical or not", "allOf": [{"$ref": "#/components/schemas/TemplateResponse"}]}, "preference": {"description": "The preferences of the subscriber regarding the related workflow", "allOf": [{"$ref": "#/components/schemas/Preference"}]}}, "required": ["preference"]}, "ChannelPreference": {"type": "object", "properties": {"type": {"type": "string", "enum": ["in_app", "email", "sms", "chat", "push"], "description": "The type of channel that is enabled or not"}, "enabled": {"type": "boolean", "description": "If channel is enabled or not"}}, "required": ["type", "enabled"]}, "UpdateSubscriberPreferenceRequestDto": {"type": "object", "properties": {"channel": {"description": "The subscriber preferences for every ChannelTypeEnum for the workflow assigned.", "allOf": [{"$ref": "#/components/schemas/ChannelPreference"}]}, "enabled": {"type": "boolean", "description": "Sets if the workflow is fully enabled for all channels or not for the subscriber."}}}, "UpdateSubscriberGlobalPreferencesRequestDto": {"type": "object", "properties": {"enabled": {"type": "boolean", "description": "Enable or disable the subscriber global preferences."}, "preferences": {"description": "The subscriber global preferences for every ChannelTypeEnum.", "type": "array", "items": {"$ref": "#/components/schemas/ChannelPreference"}}}}, "EmailBlockStyles": {"type": "object", "properties": {"textAlign": {"enum": ["left", "right", "center"], "type": "string"}}}, "EmailBlock": {"type": "object", "properties": {"type": {"enum": ["text", "button"], "type": "string"}, "content": {"type": "string"}, "url": {"type": "string"}, "styles": {"$ref": "#/components/schemas/EmailBlockStyles"}}, "required": ["type", "content"]}, "MessageCTAData": {"type": "object", "properties": {"url": {"type": "string"}}}, "MessageButton": {"type": "object", "properties": {"type": {"enum": ["primary", "secondary", "clicked"], "type": "string"}, "content": {"type": "string"}, "resultContent": {"type": "string"}}, "required": ["type", "content"]}, "MessageActionResult": {"type": "object", "properties": {"payload": {"type": "object"}, "type": {"enum": ["primary", "secondary", "clicked"], "type": "string"}}}, "MessageAction": {"type": "object", "properties": {"status": {"enum": ["pending", "done"], "type": "string"}, "buttons": {"type": "array", "items": {"$ref": "#/components/schemas/MessageButton"}}, "result": {"$ref": "#/components/schemas/MessageActionResult"}}}, "MessageCTA": {"type": "object", "properties": {"type": {"type": "string", "enum": ["redirect"]}, "data": {"$ref": "#/components/schemas/MessageCTAData"}, "action": {"$ref": "#/components/schemas/MessageAction"}}, "required": ["data"]}, "Actor": {"type": "object", "properties": {"data": {"type": "string", "nullable": true}, "type": {"enum": ["none", "user", "system_icon", "system_custom"], "type": "string"}}, "required": ["data", "type"]}, "NotificationDto": {"type": "object", "properties": {"_id": {"type": "string"}, "_templateId": {"type": "string"}, "_environmentId": {"type": "string"}, "_messageTemplateId": {"type": "string"}, "_organizationId": {"type": "string"}, "_notificationId": {"type": "string"}, "_subscriberId": {"type": "string"}, "_feedId": {"type": "string"}, "_jobId": {"type": "string"}, "createdAt": {"type": "string"}, "updatedAt": {"type": "string"}, "expireAt": {"type": "string"}, "actor": {"$ref": "#/components/schemas/Actor"}, "subscriber": {"$ref": "#/components/schemas/SubscriberResponseDto"}, "transactionId": {"type": "string"}, "templateIdentifier": {"type": "string"}, "providerId": {"type": "string"}, "content": {"type": "string"}, "subject": {"type": "string"}, "channel": {"enum": ["in_app", "email", "sms", "chat", "push"], "type": "string"}, "read": {"type": "boolean"}, "seen": {"type": "boolean"}, "deleted": {"type": "boolean"}, "deviceTokens": {"type": "array", "items": {"type": "string"}}, "cta": {"$ref": "#/components/schemas/MessageCTA"}, "status": {"type": "string", "enum": ["sent", "error", "warning"]}, "payload": {"type": "object", "description": "The payload that was used to send the notification trigger"}, "overrides": {"type": "object", "description": "Provider specific overrides used when triggering the notification"}}, "required": ["_templateId", "_environmentId", "_messageTemplateId", "_organizationId", "_notificationId", "_subscriberId", "_feedId", "_jobId", "transactionId", "content", "channel", "read", "seen", "deleted", "cta", "status", "payload", "overrides"]}, "FeedResponseDto": {"type": "object", "properties": {"_id": {"type": "string"}, "name": {"type": "string"}, "identifier": {"type": "string"}, "_environmentId": {"type": "string"}, "_organizationId": {"type": "string"}}, "required": ["name", "identifier", "_environmentId", "_organizationId"]}, "UnseenCountResponse": {"type": "object", "properties": {"count": {"type": "number"}}, "required": ["count"]}, "MessageMarkAsRequestDto": {"type": "object", "properties": {"messageId": {"oneOf": [{"type": "string"}, {"type": "array", "items": {"type": "string"}}]}, "markAs": {"enum": ["read", "seen", "unread", "unseen"], "type": "string"}}, "required": ["messageId", "markAs"]}, "MessageEntity": {"type": "object", "properties": {}}, "MarkAllMessageAsRequestDto": {"type": "object", "properties": {"feedIdentifier": {"oneOf": [{"type": "string"}, {"type": "array", "items": {"type": "string"}}], "description": "Optional feed identifier or array of feed identifiers"}, "markAs": {"enum": ["read", "seen", "unread", "unseen"], "type": "string", "description": "Mark all subscriber messages as read, unread, seen or unseen"}}, "required": ["markAs"]}, "MessageResponseDto": {"type": "object", "properties": {"_id": {"type": "string"}, "_templateId": {"type": "string"}, "_environmentId": {"type": "string"}, "_messageTemplateId": {"type": "string"}, "_organizationId": {"type": "string"}, "_notificationId": {"type": "string"}, "_subscriberId": {"type": "string"}, "subscriber": {"$ref": "#/components/schemas/SubscriberResponseDto"}, "template": {"$ref": "#/components/schemas/WorkflowResponse"}, "templateIdentifier": {"type": "string"}, "createdAt": {"type": "string"}, "lastSeenDate": {"type": "string"}, "lastReadDate": {"type": "string"}, "content": {"oneOf": [{"$ref": "#/components/schemas/EmailBlock"}, {"type": "string"}]}, "transactionId": {"type": "string"}, "subject": {"type": "string"}, "channel": {"enum": ["in_app", "email", "sms", "chat", "push"], "type": "string"}, "read": {"type": "boolean"}, "seen": {"type": "boolean"}, "email": {"type": "string"}, "phone": {"type": "string"}, "directWebhookUrl": {"type": "string"}, "providerId": {"type": "string"}, "deviceTokens": {"type": "array", "items": {"type": "string"}}, "title": {"type": "string"}, "cta": {"$ref": "#/components/schemas/MessageCTA"}, "_feedId": {"type": "string", "nullable": true}, "status": {"type": "string", "enum": ["sent", "error", "warning"]}, "errorId": {"type": "string"}, "errorText": {"type": "string"}, "payload": {"type": "object", "description": "The payload that was used to send the notification trigger"}, "overrides": {"type": "object", "description": "Provider specific overrides used when triggering the notification"}}, "required": ["_templateId", "_environmentId", "_messageTemplateId", "_organizationId", "_notificationId", "_subscriberId", "createdAt", "content", "transactionId", "channel", "read", "seen", "cta", "status", "errorId", "errorText", "payload", "overrides"]}, "MarkMessageActionAsSeenDto": {"type": "object", "properties": {"status": {"enum": ["pending", "done"], "type": "string", "description": "Message action status"}, "payload": {"type": "object", "description": "Message action payload"}}, "required": ["status"]}, "CreateFeedRequestDto": {"type": "object", "properties": {"name": {"type": "string"}}, "required": ["name"]}, "CreateLayoutResponseDto": {"type": "object", "properties": {"_id": {"type": "string"}}, "required": ["_id"]}, "GetLayoutResponseDto": {"type": "object", "properties": {"_id": {"type": "string"}, "_organizationId": {"type": "string"}, "_environmentId": {"type": "string"}, "_creatorId": {"type": "string"}, "name": {"type": "string"}, "identifier": {"type": "string"}, "description": {"type": "string"}, "channel": {"enum": ["in_app", "email", "sms", "chat", "push"], "type": "string"}, "content": {"type": "string"}, "contentType": {"type": "string"}, "variables": {"type": "array", "items": {"type": "object"}}, "isDefault": {"type": "boolean"}, "isDeleted": {"type": "boolean"}, "createdAt": {"type": "string"}, "updatedAt": {"type": "string"}, "_parentId": {"type": "string"}}, "required": ["_organizationId", "_environmentId", "_creatorId", "name", "identifier", "channel", "content", "contentType", "isDefault", "isDeleted"]}, "UpdateLayoutResponseDto": {"type": "object", "properties": {"_id": {"type": "string"}, "_organizationId": {"type": "string"}, "_environmentId": {"type": "string"}, "_creatorId": {"type": "string"}, "name": {"type": "string"}, "identifier": {"type": "string"}, "description": {"type": "string"}, "channel": {"enum": ["in_app", "email", "sms", "chat", "push"], "type": "string"}, "content": {"type": "string"}, "contentType": {"type": "string"}, "variables": {"type": "array", "items": {"type": "object"}}, "isDefault": {"type": "boolean"}, "isDeleted": {"type": "boolean"}, "createdAt": {"type": "string"}, "updatedAt": {"type": "string"}, "_parentId": {"type": "string"}}, "required": ["_organizationId", "_environmentId", "_creatorId", "name", "identifier", "channel", "content", "contentType", "isDefault", "isDeleted"]}, "UpdateLayoutRequestDto": {"type": "object", "properties": {"name": {"type": "string", "description": "User defined custom name and provided by the user that will name the Layout updated."}, "identifier": {"type": "string", "description": "User defined custom key that will be a unique identifier for the Layout updated."}, "description": {"type": "string", "description": "User defined description of the layout"}, "content": {"type": "string", "description": "User defined content for the layout."}, "variables": {"description": "User defined variables to render in the layout placeholders.", "type": "array", "items": {"type": "object"}}, "isDefault": {"type": "boolean", "description": "Variable that defines if the layout is chosen as default when creating a layout."}}, "required": ["identifier"]}, "DeleteMessageResponseDto": {"type": "object", "properties": {"acknowledged": {"type": "boolean", "description": "A boolean stating the success of the action"}, "status": {"type": "string", "description": "The status enum for the performed action", "enum": ["deleted"]}}, "required": ["acknowledged", "status"]}, "CreateTopicResponseDto": {"type": "object", "properties": {}}, "CreateTopicRequestDto": {"type": "object", "properties": {"key": {"type": "string", "description": "User defined custom key and provided by the user that will be an unique identifier for the Topic created."}, "name": {"type": "string", "description": "User defined custom name and provided by the user that will name the Topic created."}}, "required": ["key", "name"]}, "AddSubscribersRequestDto": {"type": "object", "properties": {"subscribers": {"description": "List of subscriber identifiers that will be associated to the topic", "type": "array", "items": {"type": "string"}}}, "required": ["subscribers"]}, "TopicSubscriberDto": {"type": "object", "properties": {"_organizationId": {"type": "string"}, "_environmentId": {"type": "string"}, "_subscriberId": {"type": "string"}, "_topicId": {"type": "string"}, "topicKey": {"type": "string"}, "externalSubscriberId": {"type": "string"}}, "required": ["_organizationId", "_environmentId", "_subscriberId", "_topicId", "topicKey", "externalSubscriberId"]}, "RemoveSubscribersRequestDto": {"type": "object", "properties": {"subscribers": {"description": "List of subscriber identifiers that will be removed to the topic", "type": "array", "items": {"type": "string"}}}, "required": ["subscribers"]}, "TopicDto": {"type": "object", "properties": {"_id": {"type": "string"}, "_organizationId": {"type": "string"}, "_environmentId": {"type": "string"}, "key": {"type": "string"}, "name": {"type": "string"}, "subscribers": {"type": "array", "items": {"type": "string"}}}, "required": ["_organizationId", "_environmentId", "key", "name", "subscribers"]}, "FilterTopicsResponseDto": {"type": "object", "properties": {"data": {"type": "array", "items": {"$ref": "#/components/schemas/TopicDto"}}, "page": {"type": "number"}, "pageSize": {"type": "number"}, "totalCount": {"type": "number"}}, "required": ["data", "page", "pageSize", "totalCount"]}, "GetTopicResponseDto": {"type": "object", "properties": {"_id": {"type": "string"}, "_organizationId": {"type": "string"}, "_environmentId": {"type": "string"}, "key": {"type": "string"}, "name": {"type": "string"}, "subscribers": {"type": "array", "items": {"type": "string"}}}, "required": ["_organizationId", "_environmentId", "key", "name", "subscribers"]}, "RenameTopicResponseDto": {"type": "object", "properties": {"_id": {"type": "string"}, "_organizationId": {"type": "string"}, "_environmentId": {"type": "string"}, "key": {"type": "string"}, "name": {"type": "string"}, "subscribers": {"type": "array", "items": {"type": "string"}}}, "required": ["_organizationId", "_environmentId", "key", "name", "subscribers"]}, "RenameTopicRequestDto": {"type": "object", "properties": {"name": {"type": "string", "description": "User defined custom name and provided by the user to rename the topic."}}, "required": ["name"]}, "GetTenantResponseDto": {"type": "object", "properties": {"_id": {"type": "string"}, "identifier": {"type": "string"}, "name": {"type": "string"}, "data": {"type": "object"}, "_environmentId": {"type": "string"}, "createdAt": {"type": "string"}, "updatedAt": {"type": "string"}}, "required": ["_id", "identifier", "_environmentId", "createdAt", "updatedAt"]}, "CreateTenantResponseDto": {"type": "object", "properties": {"_id": {"type": "string"}, "identifier": {"type": "string"}, "name": {"type": "string"}, "data": {"type": "object"}, "_environmentId": {"type": "string"}, "createdAt": {"type": "string"}, "updatedAt": {"type": "string"}}, "required": ["_id", "identifier", "_environmentId", "createdAt", "updatedAt"]}, "CreateTenantRequestDto": {"type": "object", "properties": {"identifier": {"type": "string"}, "name": {"type": "string"}, "data": {"type": "object"}}, "required": ["identifier", "name"]}, "UpdateTenantResponseDto": {"type": "object", "properties": {"_id": {"type": "string"}, "identifier": {"type": "string"}, "name": {"type": "string"}, "data": {"type": "object"}, "_environmentId": {"type": "string"}, "createdAt": {"type": "string"}, "updatedAt": {"type": "string"}}, "required": ["_id", "identifier", "_environmentId", "createdAt", "updatedAt"]}, "UpdateTenantRequestDto": {"type": "object", "properties": {"identifier": {"type": "string"}, "name": {"type": "string"}, "data": {"type": "object"}}}}, "headers": {"Content-Type": {"required": true, "description": "The MIME type of the response body.", "schema": {"type": "string"}, "example": "application/json"}, "RateLimit-Limit": {"required": false, "description": "The number of requests that the client is permitted to make per second. The actual maximum may differ when burst is enabled.", "schema": {"type": "string"}, "example": "100"}, "RateLimit-Remaining": {"required": false, "description": "The number of requests remaining until the next window.", "schema": {"type": "string"}, "example": "93"}, "RateLimit-Reset": {"required": false, "description": "The remaining seconds until a request of the same cost will be refreshed.", "schema": {"type": "string"}, "example": "8"}, "RateLimit-Policy": {"required": false, "description": "The rate limit policy that was used to evaluate the request.", "schema": {"type": "string"}, "example": "100;w=1;burst=110;comment=\"token bucket\";category=\"trigger\";cost=\"single\""}, "Retry-After": {"required": false, "description": "The number of seconds after which the client may retry the request that was previously rejected.", "schema": {"type": "string"}, "example": "8"}, "Idempotency-Key": {"required": false, "description": "The idempotency key used to evaluate the request.", "schema": {"type": "string"}, "example": "8"}, "Idempotency-Replay": {"required": false, "description": "Whether the request was a replay of a previous request.", "schema": {"type": "string"}, "example": "true"}, "Link": {"required": false, "description": "A link to the documentation.", "schema": {"type": "string"}, "example": "https://docs.novu.co/"}}}, "externalDocs": {"description": "Novu Documentation", "url": "https://docs.novu.co"}, "x-speakeasy-name-override": [{"operationId": "^.*get.*", "methodNameOverride": "retrieve"}, {"operationId": "^.*retrieve.*", "methodNameOverride": "retrieve"}, {"operationId": "^.*create.*", "methodNameOverride": "create"}, {"operationId": "^.*update.*", "methodNameOverride": "update"}, {"operationId": "^.*list.*", "methodNameOverride": "list"}, {"operationId": "^.*delete.*", "methodNameOverride": "delete"}, {"operationId": "^.*remove.*", "methodNameOverride": "delete"}], "x-speakeasy-retries": {"strategy": "backoff", "backoff": {"initialInterval": 500, "maxInterval": 30000, "maxElapsedTime": 3600000, "exponent": 1.5}, "statusCodes": ["408", "409", "429", "5XX"], "retryConnectionErrors": true}} +{ + "openapi": "3.0.0", + "paths": { + "/v1/organizations": { + "post": { + "operationId": "OrganizationController_createOrganization", + "summary": "Create an organization", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/CreateOrganizationDto" } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OrganizationResponseDto" + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Organizations"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "OrganizationController_createOrganization", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.organizations.create({\n name: \"\",\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + }, + "get": { + "operationId": "OrganizationController_listOrganizations", + "summary": "Fetch all organizations", + "parameters": [], + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OrganizationResponseDto" + } + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Organizations"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "OrganizationController_listOrganizations", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.organizations.list();\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + }, + "patch": { + "operationId": "OrganizationController_rename", + "x-speakeasy-name-override": "rename", + "summary": "Rename organization name", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/RenameOrganizationDto" } + } + } + }, + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RenameOrganizationDto" + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Organizations"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "OrganizationController_rename", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.organizations.rename({\n name: \"\",\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/organizations/me": { + "get": { + "operationId": "OrganizationController_getSelfOrganizationData", + "summary": "Fetch current organization details", + "parameters": [], + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OrganizationResponseDto" + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Organizations"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "OrganizationController_getSelfOrganizationData", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.organizations.retrieve();\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/organizations/members/{memberId}": { + "delete": { + "operationId": "OrganizationController_remove", + "x-speakeasy-group": "Organizations.Members", + "summary": "Remove a member from organization using memberId", + "parameters": [ + { + "name": "memberId", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/MemberResponseDto" } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Organizations"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "OrganizationController_remove", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.organizations.members.delete(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/organizations/members": { + "get": { + "operationId": "OrganizationController_listOrganizationMembers", + "x-speakeasy-group": "Organizations.Members", + "summary": "Fetch all members of current organizations", + "parameters": [], + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { "$ref": "#/components/schemas/MemberResponseDto" } + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Organizations"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "OrganizationController_listOrganizationMembers", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.organizations.members.list();\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/organizations/branding": { + "put": { + "operationId": "OrganizationController_updateBrandingDetails", + "x-speakeasy-group": "Organizations.Branding", + "summary": "Update organization branding details", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateBrandingDetailsDto" + } + } + } + }, + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OrganizationBrandingResponseDto" + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Organizations"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "OrganizationController_updateBrandingDetails", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.organizations.branding.update({\n logo: \"\",\n color: \"fuchsia\",\n fontColor: \"\",\n contentBackground: \"\",\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/environments/me": { + "get": { + "operationId": "EnvironmentsController_getCurrentEnvironment", + "summary": "Get current environment", + "parameters": [], + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EnvironmentResponseDto" + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Environments"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "EnvironmentsController_getCurrentEnvironment", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.environments.retrieve();\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/environments": { + "get": { + "operationId": "EnvironmentsController_listMyEnvironments", + "summary": "Get environments", + "parameters": [], + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EnvironmentResponseDto" + } + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Environments"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "EnvironmentsController_listMyEnvironments", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.environments.list();\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/environments/api-keys": { + "get": { + "operationId": "EnvironmentsController_listOrganizationApiKeys", + "x-speakeasy-group": "Environments.ApiKeys", + "summary": "Get api keys", + "parameters": [], + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { "$ref": "#/components/schemas/ApiKey" } + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Environments"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "EnvironmentsController_listOrganizationApiKeys", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.environments.apiKeys.list();\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/environments/api-keys/regenerate": { + "post": { + "operationId": "EnvironmentsController_regenerateOrganizationApiKeys", + "x-speakeasy-name-override": "regenerate", + "x-speakeasy-group": "Environments.ApiKeys", + "summary": "Regenerate api keys", + "parameters": [], + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { "$ref": "#/components/schemas/ApiKey" } + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Environments"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "EnvironmentsController_regenerateOrganizationApiKeys", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.environments.apiKeys.regenerate();\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/execution-details": { + "get": { + "operationId": "ExecutionDetailsController_getExecutionDetailsForNotification", + "summary": "Get execution details", + "parameters": [ + { + "name": "notificationId", + "required": true, + "in": "query", + "schema": { "type": "string" } + }, + { + "name": "subscriberId", + "required": true, + "in": "query", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ExecutionDetailsResponseDto" + } + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Execution Details"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "ExecutionDetailsController_getExecutionDetailsForNotification", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.executionDetails.retrieve(\"\", \"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/workflows": { + "get": { + "operationId": "WorkflowController_listWorkflows", + "summary": "Get workflows", + "description": "Workflows were previously named notification templates", + "parameters": [ + { + "name": "page", + "required": false, + "in": "query", + "schema": { "type": "number" } + }, + { + "name": "limit", + "required": false, + "in": "query", + "schema": { "maximum": 100, "default": 10, "type": "number" } + }, + { + "name": "query", + "required": false, + "in": "query", + "description": "A query string to filter the results. It allows filtering based on either the name or trigger identifier of the workflow items.", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WorkflowsResponseDto" + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Workflows"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "WorkflowController_listWorkflows", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.workflows.list({});\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + }, + "post": { + "operationId": "WorkflowController_create", + "summary": "Create workflow", + "description": "Workflow was previously named notification template", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateWorkflowRequestDto" + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/WorkflowResponse" } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Workflows"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "WorkflowController_create", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.workflows.create({\n name: \"\",\n notificationGroupId: \"\",\n steps: [\n {},\n ],\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/workflows/{workflowId}": { + "put": { + "operationId": "WorkflowController_updateWorkflowById", + "summary": "Update workflow", + "description": "Workflow was previously named notification template", + "parameters": [ + { + "name": "workflowId", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateWorkflowRequestDto" + } + } + } + }, + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/WorkflowResponse" } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Workflows"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "WorkflowController_updateWorkflowById", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.workflows.update(\"\", {\n name: \"\",\n notificationGroupId: \"\",\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + }, + "delete": { + "operationId": "WorkflowController_deleteWorkflowById", + "summary": "Delete workflow", + "description": "Workflow was previously named notification template", + "parameters": [ + { + "name": "workflowId", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/DataBooleanDto" } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Workflows"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "WorkflowController_deleteWorkflowById", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.workflows.delete(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + }, + "get": { + "operationId": "WorkflowController_getWorkflowById", + "summary": "Get workflow", + "description": "Workflow was previously named notification template", + "parameters": [ + { + "name": "workflowId", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/WorkflowResponse" } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Workflows"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "WorkflowController_getWorkflowById", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.workflows.retrieve(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/workflows/variables": { + "get": { + "operationId": "WorkflowController_getWorkflowVariables", + "x-speakeasy-group": "Workflows.Variables", + "summary": "Get available variables", + "description": "Get the variables that can be used in the workflow", + "parameters": [], + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VariablesResponseDto" + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Workflows"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "WorkflowController_getWorkflowVariables", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.workflows.variables.retrieve();\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/workflows/{workflowId}/status": { + "put": { + "operationId": "WorkflowController_updateActiveStatus", + "x-speakeasy-group": "Workflows.Status", + "summary": "Update workflow status", + "description": "Workflow was previously named notification template", + "parameters": [ + { + "name": "workflowId", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChangeWorkflowStatusRequestDto" + } + } + } + }, + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/WorkflowResponse" } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Workflows"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "WorkflowController_updateActiveStatus", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.workflows.status.update(\"\", {\n active: false,\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/events/trigger": { + "post": { + "operationId": "EventsController_trigger", + "x-speakeasy-group": "", + "x-speakeasy-usage-example": { "title": "Trigger Notification Event" }, + "x-speakeasy-name-override": "trigger", + "summary": "Trigger event", + "description": "\n Trigger event is the main (and only) way to send notifications to subscribers. \n The trigger identifier is used to match the particular workflow associated with it. \n Additional information can be passed according the body interface below.\n ", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TriggerEventRequestDto" + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TriggerEventResponseDto" + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Events"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "EventsController_trigger", + "source": "import { Novu } from \"@novu/api\";\nimport { TopicPayloadDtoType } from \"@novu/api/models/components\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.trigger({\n name: \"workflow_identifier\",\n payload: {},\n overrides: {},\n to: [\n {\n topicKey: \"topic_key\",\n type: TopicPayloadDtoType.Topic,\n },\n ],\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/events/trigger/bulk": { + "post": { + "operationId": "EventsController_triggerBulk", + "x-speakeasy-name-override": "triggerBulk", + "summary": "Bulk trigger event", + "description": "\n Using this endpoint you can trigger multiple events at once, to avoid multiple calls to the API.\n The bulk API is limited to 100 events per request.\n ", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/BulkTriggerEventDto" } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TriggerEventResponseDto" + } + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Events"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "EventsController_triggerBulk", + "source": "import { Novu } from \"@novu/api\";\nimport { TopicPayloadDtoType } from \"@novu/api/models/components\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.events.triggerBulk({\n events: [\n {\n name: \"workflow_identifier\",\n payload: {},\n overrides: {},\n to: [\n {\n topicKey: \"topic_key\",\n type: TopicPayloadDtoType.Topic,\n },\n ],\n },\n ],\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/events/trigger/broadcast": { + "post": { + "operationId": "EventsController_broadcastEventToAll", + "x-speakeasy-name-override": "triggerBroadcast", + "summary": "Broadcast event to all", + "description": "Trigger a broadcast event to all existing subscribers, could be used to send announcements, etc.\n In the future could be used to trigger events to a subset of subscribers based on defined filters.", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TriggerEventToAllRequestDto" + } + } + } + }, + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TriggerEventResponseDto" + } + } + } + }, + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TriggerEventResponseDto" + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Events"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "EventsController_broadcastEventToAll", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.events.triggerBroadcast({\n name: \"\",\n payload: {},\n overrides: {},\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/events/trigger/{transactionId}": { + "delete": { + "operationId": "EventsController_cancel", + "x-speakeasy-name-override": "cancel", + "summary": "Cancel triggered event", + "description": "\n Using a previously generated transactionId during the event trigger,\n will cancel any active or pending workflows. This is useful to cancel active digests, delays etc...\n ", + "parameters": [ + { + "name": "transactionId", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/DataBooleanDto" } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Events"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "EventsController_cancel", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.events.cancel(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/notifications": { + "get": { + "operationId": "NotificationsController_listNotifications", + "summary": "Get notifications", + "parameters": [ + { + "name": "channels", + "required": true, + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string", + "enum": ["in_app", "email", "sms", "chat", "push"] + } + } + }, + { + "name": "templates", + "required": true, + "in": "query", + "schema": { "type": "array", "items": { "type": "string" } } + }, + { + "name": "emails", + "required": true, + "in": "query", + "schema": { "type": "array", "items": { "type": "string" } } + }, + { + "name": "search", + "required": true, + "in": "query", + "deprecated": true, + "schema": { "type": "string" } + }, + { + "name": "subscriberIds", + "required": true, + "in": "query", + "schema": { "type": "array", "items": { "type": "string" } } + }, + { + "name": "page", + "required": false, + "in": "query", + "schema": { "default": 0, "type": "number" } + }, + { + "name": "transactionId", + "required": false, + "in": "query", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ActivitiesResponseDto" + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Notifications"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "NotificationsController_listNotifications", + "source": "import { Novu } from \"@novu/api\";\nimport { Channels } from \"@novu/api/models/operations\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.notifications.list({\n channels: [\n Channels.Chat,\n ],\n templates: [\n \"\",\n ],\n emails: [\n \"\",\n ],\n search: \"\",\n subscriberIds: [\n \"\",\n ],\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/notifications/stats": { + "get": { + "operationId": "NotificationsController_getActivityStats", + "x-speakeasy-group": "Notifications.Stats", + "summary": "Get notification statistics", + "parameters": [], + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ActivityStatsResponseDto" + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Notifications"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "NotificationsController_getActivityStats", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.notifications.stats.retrieve();\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/notifications/graph/stats": { + "get": { + "operationId": "NotificationsController_getActivityGraphStats", + "x-speakeasy-name-override": "graph", + "x-speakeasy-group": "Notifications.Stats", + "summary": "Get notification graph statistics", + "parameters": [ + { + "name": "days", + "required": false, + "in": "query", + "schema": { "type": "number" } + } + ], + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ActivityGraphStatesResponse" + } + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Notifications"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "NotificationsController_getActivityGraphStats", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.notifications.stats.graph(4018.61);\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/notifications/{notificationId}": { + "get": { + "operationId": "NotificationsController_getNotification", + "summary": "Get notification", + "parameters": [ + { + "name": "notificationId", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ActivityNotificationResponseDto" + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Notifications"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "NotificationsController_getNotification", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.notifications.retrieve(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/notification-groups": { + "post": { + "operationId": "NotificationGroupsController_createNotificationGroup", + "summary": "Create workflow group", + "description": "workflow group was previously named notification group", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateNotificationGroupRequestDto" + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotificationGroupResponseDto" + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Workflow groups"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "NotificationGroupsController_createNotificationGroup", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.workflowGroups.create({\n name: \"\",\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + }, + "get": { + "operationId": "NotificationGroupsController_listNotificationGroups", + "summary": "Get workflow groups", + "description": "workflow group was previously named notification group", + "parameters": [], + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NotificationGroupResponseDto" + } + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Workflow groups"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "NotificationGroupsController_listNotificationGroups", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.workflowGroups.list();\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/notification-groups/{id}": { + "get": { + "operationId": "NotificationGroupsController_getNotificationGroup", + "summary": "Get workflow group", + "description": "workflow group was previously named notification group", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotificationGroupResponseDto" + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Workflow groups"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "NotificationGroupsController_getNotificationGroup", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.workflowGroups.retrieve(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + }, + "patch": { + "operationId": "NotificationGroupsController_updateNotificationGroup", + "summary": "Update workflow group", + "description": "workflow group was previously named notification group", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateNotificationGroupRequestDto" + } + } + } + }, + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotificationGroupResponseDto" + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Workflow groups"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "NotificationGroupsController_updateNotificationGroup", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.workflowGroups.update(\"\", {\n name: \"\",\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + }, + "delete": { + "operationId": "NotificationGroupsController_deleteNotificationGroup", + "summary": "Delete workflow group", + "description": "workflow group was previously named notification group", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeleteNotificationGroupResponseDto" + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Workflow groups"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "NotificationGroupsController_deleteNotificationGroup", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.workflowGroups.delete(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/integrations": { + "get": { + "operationId": "IntegrationsController_listIntegrations", + "summary": "Get integrations", + "description": "Return all the integrations the user has created for that organization. Review v.0.17.0 changelog for a breaking change", + "parameters": [], + "responses": { + "200": { + "description": "The list of integrations belonging to the organization that are successfully returned.", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IntegrationResponseDto" + } + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Integrations"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "IntegrationsController_listIntegrations", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.integrations.list();\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + }, + "post": { + "operationId": "IntegrationsController_createIntegration", + "summary": "Create integration", + "description": "Create an integration for the current environment the user is based on the API key provided", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateIntegrationRequestDto" + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IntegrationResponseDto" + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Integrations"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "IntegrationsController_createIntegration", + "source": "import { Novu } from \"@novu/api\";\nimport { CreateIntegrationRequestDtoChannel } from \"@novu/api/models/components\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.integrations.create({\n providerId: \"\",\n channel: CreateIntegrationRequestDtoChannel.Sms,\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/integrations/active": { + "get": { + "operationId": "IntegrationsController_getActiveIntegrations", + "x-speakeasy-name-override": "listActive", + "summary": "Get active integrations", + "description": "Return all the active integrations the user has created for that organization. Review v.0.17.0 changelog for a breaking change", + "parameters": [], + "responses": { + "200": { + "description": "The list of active integrations belonging to the organization that are successfully returned.", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IntegrationResponseDto" + } + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Integrations"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "IntegrationsController_getActiveIntegrations", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.integrations.listActive();\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/integrations/webhook/provider/{providerOrIntegrationId}/status": { + "get": { + "operationId": "IntegrationsController_getWebhookSupportStatus", + "x-speakeasy-group": "Integrations.Webhooks", + "summary": "Get webhook support status for provider", + "description": "Return the status of the webhook for this provider, if it is supported or if it is not based on a boolean value", + "parameters": [ + { + "name": "providerOrIntegrationId", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "The status of the webhook for the provider requested", + "content": { + "application/json": { "schema": { "type": "boolean" } } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Integrations"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "IntegrationsController_getWebhookSupportStatus", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.integrations.webhooks.retrieve(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/integrations/{integrationId}": { + "put": { + "operationId": "IntegrationsController_updateIntegrationById", + "summary": "Update integration", + "parameters": [ + { + "name": "integrationId", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateIntegrationRequestDto" + } + } + } + }, + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IntegrationResponseDto" + } + } + } + }, + "404": { + "description": "The integration with the integrationId provided does not exist in the database." + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Integrations"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "IntegrationsController_updateIntegrationById", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.integrations.update(\"\", {});\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + }, + "delete": { + "operationId": "IntegrationsController_removeIntegration", + "summary": "Delete integration", + "parameters": [ + { + "name": "integrationId", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IntegrationResponseDto" + } + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Integrations"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "IntegrationsController_removeIntegration", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.integrations.delete(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/integrations/{integrationId}/set-primary": { + "post": { + "operationId": "IntegrationsController_setIntegrationAsPrimary", + "x-speakeasy-name-override": "setAsPrimary", + "summary": "Set integration as primary", + "parameters": [ + { + "name": "integrationId", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IntegrationResponseDto" + } + } + } + }, + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IntegrationResponseDto" + } + } + } + }, + "404": { + "description": "The integration with the integrationId provided does not exist in the database." + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Integrations"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "IntegrationsController_setIntegrationAsPrimary", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.integrations.setAsPrimary(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/changes": { + "get": { + "operationId": "ChangesController_getChanges", + "summary": "Get changes", + "parameters": [ + { + "name": "page", + "required": false, + "in": "query", + "schema": { "type": "number" } + }, + { + "name": "limit", + "required": false, + "in": "query", + "schema": { "maximum": 100, "default": 10, "type": "number" } + }, + { + "name": "promoted", + "required": true, + "in": "query", + "schema": { "default": "false", "type": "string" } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/ChangesResponseDto" } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Changes"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "ChangesController_getChanges", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.changes.retrieve({\n promoted: \"false\",\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/changes/count": { + "get": { + "operationId": "ChangesController_getChangesCount", + "x-speakeasy-name-override": "count", + "summary": "Get changes count", + "parameters": [], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/DataNumberDto" } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Changes"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "ChangesController_getChangesCount", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.changes.count();\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/changes/bulk/apply": { + "post": { + "operationId": "ChangesController_bulkApplyDiff", + "x-speakeasy-name-override": "applyBulk", + "summary": "Apply changes", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/BulkApplyChangeDto" } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { "$ref": "#/components/schemas/ChangeResponseDto" } + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Changes"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "ChangesController_bulkApplyDiff", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.changes.applyBulk({\n changeIds: [\n \"\",\n ],\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/changes/{changeId}/apply": { + "post": { + "operationId": "ChangesController_applyDiff", + "x-speakeasy-name-override": "apply", + "summary": "Apply change", + "parameters": [ + { + "name": "changeId", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { "$ref": "#/components/schemas/ChangeResponseDto" } + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Changes"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "ChangesController_applyDiff", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.changes.apply(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/subscribers": { + "get": { + "operationId": "SubscribersController_listSubscribers", + "x-speakeasy-pagination": { + "type": "offsetLimit", + "inputs": [ + { "name": "page", "in": "parameters", "type": "page" }, + { "name": "limit", "in": "parameters", "type": "limit" } + ], + "outputs": { "results": "$.data.resultArray" } + }, + "summary": "Get subscribers", + "description": "Returns a list of subscribers, could paginated using the `page` and `limit` query parameter", + "parameters": [ + { + "name": "page", + "required": false, + "in": "query", + "schema": { "type": "number" } + }, + { + "name": "limit", + "required": false, + "in": "query", + "schema": { "maximum": 100, "default": 10, "type": "number" } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "allOf": [ + { "$ref": "#/components/schemas/PaginatedResponseDto" }, + { + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SubscriberResponseDto" + } + } + } + } + ] + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Subscribers"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "SubscribersController_listSubscribers", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.list(7685.78, 10);\n\n for await (const page of result) {\n // handle page\n }\n}\n\nrun();" + } + ] + }, + "post": { + "operationId": "SubscribersController_createSubscriber", + "summary": "Create subscriber", + "description": "Creates a subscriber entity, in the Novu platform. The subscriber will be later used to receive notifications, and access notification feeds. Communication credentials such as email, phone number, and 3 rd party credentials i.e slack tokens could be later associated to this entity.", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateSubscriberRequestDto" + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SubscriberResponseDto" + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Subscribers"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "SubscribersController_createSubscriber", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.create({\n subscriberId: \"\",\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/subscribers/{subscriberId}": { + "get": { + "operationId": "SubscribersController_getSubscriber", + "summary": "Get subscriber", + "description": "Get subscriber by your internal id used to identify the subscriber", + "parameters": [ + { + "name": "subscriberId", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SubscriberResponseDto" + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Subscribers"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "SubscribersController_getSubscriber", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.retrieve(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + }, + "put": { + "operationId": "SubscribersController_updateSubscriber", + "summary": "Update subscriber", + "description": "Used to update the subscriber entity with new information", + "parameters": [ + { + "name": "subscriberId", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateSubscriberRequestDto" + } + } + } + }, + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SubscriberResponseDto" + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Subscribers"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "SubscribersController_updateSubscriber", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.update(\"\", {});\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + }, + "delete": { + "operationId": "SubscribersController_removeSubscriber", + "summary": "Delete subscriber", + "description": "Deletes a subscriber entity from the Novu platform", + "parameters": [ + { + "name": "subscriberId", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeleteSubscriberResponseDto" + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Subscribers"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "SubscribersController_removeSubscriber", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.delete(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/subscribers/bulk": { + "post": { + "operationId": "SubscribersController_bulkCreateSubscribers", + "x-speakeasy-name-override": "createBulk", + "summary": "Bulk create subscribers", + "description": "\n Using this endpoint you can create multiple subscribers at once, to avoid multiple calls to the API.\n The bulk API is limited to 500 subscribers per request.\n ", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BulkSubscriberCreateDto" + } + } + } + }, + "responses": { + "201": { "description": "" }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Subscribers"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "SubscribersController_bulkCreateSubscribers", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n await novu.subscribers.createBulk({\n subscribers: [\n {\n subscriberId: \"\",\n },\n ],\n });\n\n \n}\n\nrun();" + } + ] + } + }, + "/v1/subscribers/{subscriberId}/credentials": { + "put": { + "operationId": "SubscribersController_updateSubscriberChannel", + "x-speakeasy-group": "Subscribers.Credentials", + "summary": "Update subscriber credentials", + "description": "Subscriber credentials associated to the delivery methods such as slack and push tokens.", + "parameters": [ + { + "name": "subscriberId", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateSubscriberChannelRequestDto" + } + } + } + }, + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SubscriberResponseDto" + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Subscribers"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "SubscribersController_updateSubscriberChannel", + "source": "import { Novu } from \"@novu/api\";\nimport { UpdateSubscriberChannelRequestDtoProviderId } from \"@novu/api/models/components\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.credentials.update(\"\", {\n providerId: UpdateSubscriberChannelRequestDtoProviderId.Pushpad,\n credentials: {\n webhookUrl: \"\",\n },\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + }, + "patch": { + "operationId": "SubscribersController_modifySubscriberChannel", + "x-speakeasy-name-override": "append", + "x-speakeasy-group": "Subscribers.Credentials", + "summary": "Modify subscriber credentials", + "description": "Subscriber credentials associated to the delivery methods such as slack and push tokens.\n This endpoint appends provided credentials and deviceTokens to the existing ones.", + "parameters": [ + { + "name": "subscriberId", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateSubscriberChannelRequestDto" + } + } + } + }, + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SubscriberResponseDto" + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Subscribers"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "SubscribersController_modifySubscriberChannel", + "source": "import { Novu } from \"@novu/api\";\nimport { UpdateSubscriberChannelRequestDtoProviderId } from \"@novu/api/models/components\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.credentials.append(\"\", {\n providerId: UpdateSubscriberChannelRequestDtoProviderId.Zulip,\n credentials: {\n webhookUrl: \"\",\n },\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/subscribers/{subscriberId}/credentials/{providerId}": { + "delete": { + "operationId": "SubscribersController_deleteSubscriberCredentials", + "x-speakeasy-group": "Subscribers.Credentials", + "summary": "Delete subscriber credentials by providerId", + "description": "Delete subscriber credentials such as slack and expo tokens.", + "parameters": [ + { + "name": "subscriberId", + "required": true, + "in": "path", + "schema": { "type": "string" } + }, + { + "name": "providerId", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "responses": { + "204": { "description": "" }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Subscribers"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "SubscribersController_deleteSubscriberCredentials", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n await novu.subscribers.credentials.delete(\"\", \"\");\n\n \n}\n\nrun();" + } + ] + } + }, + "/v1/subscribers/{subscriberId}/online-status": { + "patch": { + "operationId": "SubscribersController_updateSubscriberOnlineFlag", + "x-speakeasy-name-override": "updateOnlineFlag", + "x-speakeasy-group": "Subscribers.properties", + "summary": "Update subscriber online status", + "description": "Used to update the subscriber isOnline flag.", + "parameters": [ + { + "name": "subscriberId", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateSubscriberOnlineFlagRequestDto" + } + } + } + }, + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SubscriberResponseDto" + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Subscribers"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "SubscribersController_updateSubscriberOnlineFlag", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.properties.updateOnlineFlag(\"\", {\n isOnline: false,\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/subscribers/{subscriberId}/preferences": { + "get": { + "operationId": "SubscribersController_listSubscriberPreferences", + "x-speakeasy-group": "Subscribers.Preferences", + "summary": "Get subscriber preferences", + "parameters": [ + { + "name": "subscriberId", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/UpdateSubscriberPreferenceResponseDto" + } + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Subscribers"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "SubscribersController_listSubscriberPreferences", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.preferences.list(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + }, + "patch": { + "operationId": "SubscribersController_updateSubscriberGlobalPreferences", + "x-speakeasy-name-override": "updateGlobal", + "x-speakeasy-group": "Subscribers.Preferences", + "summary": "Update subscriber global preferences", + "parameters": [ + { + "name": "subscriberId", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateSubscriberGlobalPreferencesRequestDto" + } + } + } + }, + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateSubscriberPreferenceResponseDto" + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Subscribers"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "SubscribersController_updateSubscriberGlobalPreferences", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.preferences.updateGlobal(\"\", {});\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/subscribers/{subscriberId}/preferences/{parameter}": { + "get": { + "operationId": "SubscribersController_getSubscriberPreferenceByLevel", + "x-speakeasy-name-override": "retrieveByLevel", + "x-speakeasy-group": "Subscribers.Preferences", + "summary": "Get subscriber preferences by level", + "parameters": [ + { + "name": "parameter", + "required": true, + "in": "path", + "schema": { "type": "string" } + }, + { + "name": "subscriberId", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/GetSubscriberPreferencesResponseDto" + } + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Subscribers"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "SubscribersController_getSubscriberPreferenceByLevel", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.preferences.retrieveByLevel(\"\", \"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + }, + "patch": { + "operationId": "SubscribersController_updateSubscriberPreference", + "x-speakeasy-group": "Subscribers.Preferences", + "summary": "Update subscriber preference", + "parameters": [ + { + "name": "subscriberId", + "required": true, + "in": "path", + "schema": { "type": "string" } + }, + { + "name": "parameter", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateSubscriberPreferenceRequestDto" + } + } + } + }, + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateSubscriberPreferenceResponseDto" + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Subscribers"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "SubscribersController_updateSubscriberPreference", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.preferences.update({\n subscriberId: \"\",\n parameter: \"\",\n updateSubscriberPreferenceRequestDto: {},\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/subscribers/{subscriberId}/notifications/feed": { + "get": { + "operationId": "SubscribersController_getNotificationsFeed", + "x-speakeasy-group": "Subscribers.Notifications", + "summary": "Get in-app notification feed for a particular subscriber", + "parameters": [ + { + "name": "subscriberId", + "required": true, + "in": "path", + "schema": { "type": "string" } + }, + { + "name": "page", + "required": false, + "in": "query", + "schema": { "type": "number" } + }, + { + "name": "limit", + "required": false, + "in": "query", + "schema": { "maximum": 100, "default": 10, "type": "number" } + }, + { + "name": "read", + "required": false, + "in": "query", + "schema": { "type": "boolean" } + }, + { + "name": "seen", + "required": false, + "in": "query", + "schema": { "type": "boolean" } + }, + { + "name": "payload", + "required": false, + "in": "query", + "description": "Base64 encoded string of the partial payload JSON object", + "example": "btoa(JSON.stringify({ foo: 123 })) results in base64 encoded string like eyJmb28iOjEyM30=", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "allOf": [ + { "$ref": "#/components/schemas/PaginatedResponseDto" }, + { + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FeedResponseDto" + } + } + } + } + ] + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Subscribers"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "SubscribersController_getNotificationsFeed", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.notifications.retrieve({\n subscriberId: \"\",\n payload: \"btoa(JSON.stringify({ foo: 123 })) results in base64 encoded string like eyJmb28iOjEyM30=\",\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/subscribers/{subscriberId}/notifications/unseen": { + "get": { + "operationId": "SubscribersController_getUnseenCount", + "x-speakeasy-name-override": "unseenCount", + "x-speakeasy-group": "Subscribers.Notifications", + "summary": "Get the unseen in-app notifications count for subscribers feed", + "parameters": [ + { + "name": "seen", + "required": true, + "in": "query", + "schema": { "type": "boolean" } + }, + { + "name": "subscriberId", + "required": true, + "in": "path", + "schema": { "type": "string" } + }, + { + "name": "limit", + "required": true, + "in": "query", + "schema": { "type": "number" } + } + ], + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/UnseenCountResponse" } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Subscribers"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "SubscribersController_getUnseenCount", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.notifications.unseenCount({\n seen: false,\n subscriberId: \"\",\n limit: 2166.35,\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/subscribers/{subscriberId}/messages/mark-as": { + "post": { + "operationId": "SubscribersController_markMessagesAs", + "x-speakeasy-name-override": "markAllAs", + "x-speakeasy-group": "Subscribers.Messages", + "summary": "Mark a subscriber messages as seen, read, unseen or unread", + "parameters": [ + { + "name": "subscriberId", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MessageMarkAsRequestDto" + } + } + } + }, + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { "$ref": "#/components/schemas/MessageEntity" } + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Subscribers"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "SubscribersController_markMessagesAs", + "source": "import { Novu } from \"@novu/api\";\nimport { MarkAs } from \"@novu/api/models/components\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.messages.markAllAs(\"\", {\n messageId: \"\",\n markAs: MarkAs.Seen,\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/subscribers/{subscriberId}/messages/mark-all": { + "post": { + "operationId": "SubscribersController_markAllUnreadAsRead", + "x-speakeasy-name-override": "markAll", + "x-speakeasy-group": "Subscribers.Messages", + "summary": "Marks all the subscriber messages as read, unread, seen or unseen. Optionally you can pass feed id (or array) to mark messages of a particular feed.", + "parameters": [ + { + "name": "subscriberId", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MarkAllMessageAsRequestDto" + } + } + } + }, + "responses": { + "201": { + "description": "", + "content": { + "application/json": { "schema": { "type": "number" } } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Subscribers"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "SubscribersController_markAllUnreadAsRead", + "source": "import { Novu } from \"@novu/api\";\nimport { MarkAllMessageAsRequestDtoMarkAs } from \"@novu/api/models/components\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.messages.markAll(\"\", {\n markAs: MarkAllMessageAsRequestDtoMarkAs.Seen,\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/subscribers/{subscriberId}/messages/{messageId}/actions/{type}": { + "post": { + "operationId": "SubscribersController_markActionAsSeen", + "x-speakeasy-name-override": "updateAsSeen", + "x-speakeasy-group": "Subscribers.Messages", + "summary": "Mark message action as seen", + "parameters": [ + { + "name": "messageId", + "required": true, + "in": "path", + "schema": { "type": "string" } + }, + { + "name": "type", + "required": true, + "in": "path", + "schema": { "type": "string" } + }, + { + "name": "subscriberId", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MarkMessageActionAsSeenDto" + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/MessageResponseDto" } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Subscribers"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "SubscribersController_markActionAsSeen", + "source": "import { Novu } from \"@novu/api\";\nimport { MarkMessageActionAsSeenDtoStatus } from \"@novu/api/models/components\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.messages.updateAsSeen({\n messageId: \"\",\n type: \"\",\n subscriberId: \"\",\n markMessageActionAsSeenDto: {\n status: MarkMessageActionAsSeenDtoStatus.Done,\n },\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/subscribers/{subscriberId}/credentials/{providerId}/oauth/callback": { + "get": { + "operationId": "SubscribersController_chatOauthCallback", + "x-speakeasy-name-override": "chatAccessOauthCallBack", + "x-speakeasy-group": "Subscribers.Authentication", + "summary": "Handle providers oauth redirect", + "parameters": [ + { + "name": "subscriberId", + "required": true, + "in": "path", + "schema": { "type": "string" } + }, + { + "name": "providerId", + "required": true, + "in": "path", + "schema": { "type": "string" } + }, + { + "name": "code", + "required": true, + "in": "query", + "schema": { "type": "string" } + }, + { + "name": "hmacHash", + "required": true, + "in": "query", + "schema": { "type": "string" } + }, + { + "name": "environmentId", + "required": true, + "in": "query", + "schema": { "type": "string" } + }, + { + "name": "integrationIdentifier", + "required": false, + "in": "query", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { "schema": { "type": "object" } } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Subscribers"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "SubscribersController_chatOauthCallback", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.subscribers.authentication.chatAccessOauthCallBack({\n subscriberId: \"\",\n providerId: \"\",\n code: \"\",\n hmacHash: \"\",\n environmentId: \"\",\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/subscribers/{subscriberId}/credentials/{providerId}/oauth": { + "get": { + "operationId": "SubscribersController_chatAccessOauth", + "x-speakeasy-name-override": "chatAccessOauth", + "x-speakeasy-group": "Subscribers.Authentication", + "summary": "Handle chat oauth", + "parameters": [ + { + "name": "subscriberId", + "required": true, + "in": "path", + "schema": { "type": "string" } + }, + { + "name": "providerId", + "required": true, + "in": "path", + "schema": { "type": "string" } + }, + { + "name": "hmacHash", + "required": true, + "in": "query", + "schema": { "type": "string" } + }, + { + "name": "environmentId", + "required": true, + "in": "query", + "schema": { "type": "string" } + }, + { + "name": "integrationIdentifier", + "required": false, + "in": "query", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { "description": "" }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Subscribers"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "SubscribersController_chatAccessOauth", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n await novu.subscribers.authentication.chatAccessOauth({\n subscriberId: \"\",\n providerId: \"\",\n hmacHash: \"\",\n environmentId: \"\",\n });\n\n \n}\n\nrun();" + } + ] + } + }, + "/v1/feeds": { + "post": { + "operationId": "FeedsController_createFeed", + "summary": "Create feed", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/CreateFeedRequestDto" } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/FeedResponseDto" } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Feeds"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "FeedsController_createFeed", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.feeds.create({\n name: \"\",\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + }, + "get": { + "operationId": "FeedsController_getFeeds", + "summary": "Get feeds", + "parameters": [], + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { "$ref": "#/components/schemas/FeedResponseDto" } + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Feeds"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "FeedsController_getFeeds", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.feeds.retrieve();\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/feeds/{feedId}": { + "delete": { + "operationId": "FeedsController_deleteFeedById", + "summary": "Delete feed", + "parameters": [ + { + "name": "feedId", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { "$ref": "#/components/schemas/FeedResponseDto" } + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Feeds"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "FeedsController_deleteFeedById", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.feeds.delete(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/layouts": { + "post": { + "operationId": "LayoutsController_PropertyDescriptor", + "x-speakeasy-name-override": "create", + "summary": "Layout creation", + "description": "Create a layout", + "parameters": [], + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateLayoutResponseDto" + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Layouts"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "LayoutsController_PropertyDescriptor", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.layouts.create();\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + }, + "get": { + "operationId": "LayoutsController_listLayouts", + "summary": "Filter layouts", + "description": "Returns a list of layouts that can be paginated using the `page` query parameter and filtered by the environment where it is executed from the organization the user belongs to.", + "parameters": [ + { + "name": "page", + "required": false, + "in": "query", + "description": "Number of page for pagination", + "schema": { "minimum": 0, "type": "number" } + }, + { + "name": "pageSize", + "required": false, + "in": "query", + "description": "Size of page for pagination", + "schema": { "minimum": 0, "type": "number" } + }, + { + "name": "sortBy", + "required": false, + "in": "query", + "description": "Sort field. Currently only supported `createdAt`", + "schema": { "type": "string" } + }, + { + "name": "orderBy", + "required": false, + "in": "query", + "description": "Direction of the sorting query param", + "schema": { "enum": ["ASC", "DESC"], "type": "string" } + } + ], + "responses": { + "200": { + "description": "The list of layouts that match the criteria of the query params are successfully returned." + }, + "400": { + "description": "Page size can not be larger than the page size limit." + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Layouts"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "LayoutsController_listLayouts", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n await novu.layouts.list({});\n\n \n}\n\nrun();" + } + ] + } + }, + "/v1/layouts/{layoutId}": { + "get": { + "operationId": "LayoutsController_getLayout", + "summary": "Get layout", + "description": "Get a layout by its ID", + "parameters": [ + { + "name": "layoutId", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetLayoutResponseDto" + } + } + } + }, + "404": { + "description": "The layout with the layoutId provided does not exist in the database." + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Layouts"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "LayoutsController_getLayout", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.layouts.retrieve(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + }, + "delete": { + "operationId": "LayoutsController_deleteLayout", + "summary": "Delete layout", + "description": "Execute a soft delete of a layout given a certain ID.", + "parameters": [ + { + "name": "layoutId", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "responses": { + "204": { "description": "The layout has been deleted correctly" }, + "404": { + "description": "The layout with the layoutId provided does not exist in the database so it can not be deleted." + }, + "409": { + "description": "Either you are trying to delete a layout that is being used or a layout that is the default in the environment.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Layouts"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "LayoutsController_deleteLayout", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n await novu.layouts.delete(\"\");\n\n \n}\n\nrun();" + } + ] + }, + "patch": { + "operationId": "LayoutsController_updateLayout", + "summary": "Update a layout", + "description": "Update the name, content and variables of a layout. Also change it to be default or no.", + "parameters": [ + { + "name": "layoutId", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateLayoutRequestDto" + } + } + } + }, + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateLayoutResponseDto" + } + } + } + }, + "400": { + "description": "The payload provided or the URL param are not right." + }, + "404": { + "description": "The layout with the layoutId provided does not exist in the database so it can not be updated." + }, + "409": { + "description": "One default layout is needed. If you are trying to turn a default layout as not default, you should turn a different layout as default first and automatically it will be done by the system.", + "content": { + "application/json": { + "schema": { "example": "One default layout is required" } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Layouts"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "LayoutsController_updateLayout", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.layouts.update(\"\", {\n identifier: \"\",\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/layouts/{layoutId}/default": { + "post": { + "operationId": "LayoutsController_setDefaultLayout", + "x-speakeasy-name-override": "setAsDefault", + "summary": "Set default layout", + "description": "Sets the default layout for the environment and updates to non default to the existing default layout (if any).", + "parameters": [ + { + "name": "layoutId", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "responses": { + "204": { + "description": "The selected layout has been set as the default for the environment." + }, + "404": { + "description": "The layout with the layoutId provided does not exist in the database so it can not be set as the default for the environment." + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Layouts"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "LayoutsController_setDefaultLayout", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n await novu.layouts.setAsDefault(\"\");\n\n \n}\n\nrun();" + } + ] + } + }, + "/v1/messages": { + "get": { + "operationId": "MessagesController_getMessages", + "summary": "Get messages", + "description": "Returns a list of messages, could paginate using the `page` query parameter", + "parameters": [ + { + "name": "channel", + "required": false, + "in": "query", + "schema": { + "enum": ["in_app", "email", "sms", "chat", "push"], + "type": "string" + } + }, + { + "name": "subscriberId", + "required": false, + "in": "query", + "schema": { "type": "string" } + }, + { + "name": "transactionId", + "required": false, + "in": "query", + "schema": { "type": "array", "items": { "type": "string" } } + }, + { + "name": "page", + "required": false, + "in": "query", + "schema": { "default": 0, "type": "number" } + }, + { + "name": "limit", + "required": false, + "in": "query", + "schema": { "default": 10, "type": "number" } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ActivitiesResponseDto" + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Messages"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "MessagesController_getMessages", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.messages.retrieve({});\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/messages/{messageId}": { + "delete": { + "operationId": "MessagesController_deleteMessage", + "summary": "Delete message", + "description": "Deletes a message entity from the Novu platform", + "parameters": [ + { + "name": "messageId", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeleteMessageResponseDto" + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Messages"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "MessagesController_deleteMessage", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.messages.delete(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/messages/transaction/{transactionId}": { + "delete": { + "operationId": "MessagesController_deleteMessagesByTransactionId", + "x-speakeasy-name-override": "deleteByTransactionId", + "summary": "Delete messages by transactionId", + "description": "Deletes messages entity from the Novu platform using TransactionId of message", + "parameters": [ + { + "name": "channel", + "required": false, + "in": "query", + "description": "The channel of the message to be deleted", + "schema": { + "enum": ["in_app", "email", "sms", "chat", "push"], + "type": "string" + } + }, + { + "name": "transactionId", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "responses": { + "204": { "description": "" }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Messages"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "MessagesController_deleteMessagesByTransactionId", + "source": "import { Novu } from \"@novu/api\";\nimport { QueryParamChannel } from \"@novu/api/models/operations\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n await novu.messages.deleteByTransactionId(\"\", QueryParamChannel.Push);\n\n \n}\n\nrun();" + } + ] + } + }, + "/v1/topics": { + "post": { + "operationId": "TopicsController_createTopic", + "summary": "Topic creation", + "description": "Create a topic", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/CreateTopicRequestDto" } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateTopicResponseDto" + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Topics"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "TopicsController_createTopic", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.topics.create({\n key: \"\",\n name: \"\",\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + }, + "get": { + "operationId": "TopicsController_listTopics", + "summary": "Filter topics", + "description": "Returns a list of topics that can be paginated using the `page` query parameter and filtered by the topic key with the `key` query parameter", + "parameters": [ + { + "name": "page", + "required": false, + "in": "query", + "description": "Number of page for the pagination", + "schema": { "minimum": 0, "default": 0, "type": "number" } + }, + { + "name": "pageSize", + "required": false, + "in": "query", + "description": "Size of page for the pagination", + "schema": { "minimum": 0, "default": 10, "type": "number" } + }, + { + "name": "key", + "required": false, + "in": "query", + "description": "Topic key", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FilterTopicsResponseDto" + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Topics"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "TopicsController_listTopics", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.topics.list({});\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/topics/{topicKey}/subscribers": { + "post": { + "operationId": "TopicsController_addSubscribers", + "x-speakeasy-name-override": "assign", + "x-speakeasy-group": "Topics.Subscribers", + "summary": "Subscribers addition", + "description": "Add subscribers to a topic by key", + "parameters": [ + { + "name": "topicKey", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AddSubscribersRequestDto" + } + } + } + }, + "responses": { + "200": { "description": "" }, + "204": { "description": "" }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Topics"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "TopicsController_addSubscribers", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n await novu.topics.subscribers.assign(\"\", {\n subscribers: [\n \"\",\n ],\n });\n\n \n}\n\nrun();" + } + ] + } + }, + "/v1/topics/{topicKey}/subscribers/{externalSubscriberId}": { + "get": { + "operationId": "TopicsController_getTopicSubscriber", + "x-speakeasy-group": "Topics.Subscribers", + "summary": "Check topic subscriber", + "description": "Check if a subscriber belongs to a certain topic", + "parameters": [ + { + "name": "topicKey", + "required": true, + "in": "path", + "schema": { "type": "string" } + }, + { + "name": "externalSubscriberId", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/TopicSubscriberDto" } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Topics"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "TopicsController_getTopicSubscriber", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.topics.subscribers.retrieve(\"\", \"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/topics/{topicKey}/subscribers/removal": { + "post": { + "operationId": "TopicsController_removeSubscribers", + "x-speakeasy-group": "Topics.Subscribers", + "summary": "Subscribers removal", + "description": "Remove subscribers from a topic", + "parameters": [ + { + "name": "topicKey", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RemoveSubscribersRequestDto" + } + } + } + }, + "responses": { + "204": { "description": "" }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Topics"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "TopicsController_removeSubscribers", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n await novu.topics.subscribers.delete(\"\", {\n subscribers: [\n \"\",\n ],\n });\n\n \n}\n\nrun();" + } + ] + } + }, + "/v1/topics/{topicKey}": { + "delete": { + "operationId": "TopicsController_deleteTopic", + "summary": "Delete topic", + "description": "Delete a topic by its topic key if it has no subscribers", + "parameters": [ + { + "name": "topicKey", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "responses": { + "204": { "description": "The topic has been deleted correctly" }, + "404": { + "description": "The topic with the key provided does not exist in the database so it can not be deleted." + }, + "409": { + "description": "The topic you are trying to delete has subscribers assigned to it. Delete the subscribers before deleting the topic.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Topics"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "TopicsController_deleteTopic", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n await novu.topics.delete(\"\");\n\n \n}\n\nrun();" + } + ] + }, + "get": { + "operationId": "TopicsController_getTopic", + "summary": "Get topic", + "description": "Get a topic by its topic key", + "parameters": [ + { + "name": "topicKey", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/GetTopicResponseDto" } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Topics"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "TopicsController_getTopic", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.topics.retrieve(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + }, + "patch": { + "operationId": "TopicsController_renameTopic", + "x-speakeasy-name-override": "rename", + "summary": "Rename a topic", + "description": "Rename a topic by providing a new name", + "parameters": [ + { + "name": "topicKey", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/RenameTopicRequestDto" } + } + } + }, + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RenameTopicResponseDto" + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Topics"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "TopicsController_renameTopic", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.topics.rename(\"\", {\n name: \"\",\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/tenants": { + "get": { + "operationId": "TenantController_listTenants", + "x-speakeasy-pagination": { + "type": "offsetLimit", + "inputs": [ + { "name": "page", "in": "parameters", "type": "page" }, + { "name": "limit", "in": "parameters", "type": "limit" } + ], + "outputs": { "results": "$.data.resultArray" } + }, + "summary": "Get tenants", + "description": "Returns a list of tenants, could paginated using the `page` and `limit` query parameter", + "parameters": [ + { + "name": "page", + "required": false, + "in": "query", + "schema": { "type": "number" } + }, + { + "name": "limit", + "required": false, + "in": "query", + "schema": { "maximum": 100, "default": 10, "type": "number" } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "allOf": [ + { "$ref": "#/components/schemas/PaginatedResponseDto" }, + { + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/GetTenantResponseDto" + } + } + } + } + ] + } + } + } + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Tenants"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "TenantController_listTenants", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.tenants.list(7685.78, 10);\n\n for await (const page of result) {\n // handle page\n }\n}\n\nrun();" + } + ] + }, + "post": { + "operationId": "TenantController_createTenant", + "summary": "Create tenant", + "description": "Create tenant under the current environment", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateTenantRequestDto" + } + } + } + }, + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateTenantResponseDto" + } + } + } + }, + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateTenantResponseDto" + } + } + } + }, + "409": { + "description": "A tenant with the same identifier is already exist.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Tenants"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "TenantController_createTenant", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.tenants.create({\n identifier: \"\",\n name: \"\",\n });\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + } + }, + "/v1/tenants/{identifier}": { + "get": { + "operationId": "TenantController_getTenantById", + "summary": "Get tenant", + "description": "Get tenant by your internal id used to identify the tenant", + "parameters": [ + { + "name": "identifier", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetTenantResponseDto" + } + } + } + }, + "404": { + "description": "The tenant with the identifier provided does not exist in the database." + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Tenants"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "TenantController_getTenantById", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.tenants.retrieve(\"\");\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + }, + "patch": { + "operationId": "TenantController_updateTenant", + "summary": "Update tenant", + "description": "Update tenant by your internal id used to identify the tenant", + "parameters": [ + { + "name": "identifier", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateTenantRequestDto" + } + } + } + }, + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateTenantResponseDto" + } + } + } + }, + "404": { + "description": "The tenant with the identifier provided does not exist in the database." + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Tenants"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "TenantController_updateTenant", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n const result = await novu.tenants.update(\"\", {});\n\n // Handle the result\n console.log(result)\n}\n\nrun();" + } + ] + }, + "delete": { + "operationId": "TenantController_removeTenant", + "summary": "Delete tenant", + "description": "Deletes a tenant entity from the Novu platform", + "parameters": [ + { + "name": "identifier", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "responses": { + "204": { "description": "The tenant has been deleted correctly" }, + "404": { + "description": "The tenant with the identifier provided does not exist in the database so it can not be deleted." + }, + "409": { + "description": "The request could not be completed due to a conflict with the current state of the target resource.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Request with key 3909d656-d4fe-4e80-ba86-90d3861afcd7 is currently being processed. Please retry after 1 second" + } + } + } + }, + "429": { + "description": "The client has sent too many requests in a given amount of time. ", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "API rate limit exceeded" + } + } + } + }, + "503": { + "description": "The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Please wait some time, then try again." + } + } + } + } + }, + "tags": ["Tenants"], + "security": [{ "api-key": [] }], + "x-codeSamples": [ + { + "lang": "typescript", + "label": "TenantController_removeTenant", + "source": "import { Novu } from \"@novu/api\";\n\nconst novu = new Novu({\n apiKey: \"\",\n});\n\nasync function run() {\n await novu.tenants.delete(\"\");\n\n \n}\n\nrun();" + } + ] + } + } + }, + "info": { + "title": "Novu API", + "description": "Novu REST API. Please see https://docs.novu.co/api-reference for more details.", + "version": "1.0", + "contact": { + "name": "Novu Support", + "url": "https://discord.gg/novu", + "email": "support@novu.co" + }, + "termsOfService": "https://novu.co/terms", + "license": { "name": "MIT", "url": "https://opensource.org/license/mit" } + }, + "tags": [ + { + "name": "Events", + "description": "Events represent a change in state of a subscriber. They are used to trigger workflows, and enable you to send notifications to subscribers based on their actions.", + "externalDocs": { "url": "https://docs.novu.co/workflows" } + }, + { + "name": "Subscribers", + "description": "A subscriber in Novu represents someone who should receive a message. A subscriber’s profile information contains important attributes about the subscriber that will be used in messages (name, email). The subscriber object can contain other key-value pairs that can be used to further personalize your messages.", + "externalDocs": { "url": "https://docs.novu.co/subscribers/subscribers" } + }, + { + "name": "Topics", + "description": "Topics are a way to group subscribers together so that they can be notified of events at once. A topic is identified by a custom key. This can be helpful for things like sending out marketing emails or notifying users of new features. Topics can also be used to send notifications to the subscribers who have been grouped together based on their interests, location, activities and much more.", + "externalDocs": { "url": "https://docs.novu.co/subscribers/topics" } + }, + { + "name": "Notification", + "description": "A notification conveys information from source to recipient, triggered by a workflow acting as a message blueprint. Notifications can be individual or bundled as digest for user-friendliness.", + "externalDocs": { + "url": "https://docs.novu.co/getting-started/introduction" + } + }, + { + "name": "Integrations", + "description": "With the help of the Integration Store, you can easily integrate your favorite delivery provider. During the runtime of the API, the Integrations Store is responsible for storing the configurations of all the providers.", + "externalDocs": { + "url": "https://docs.novu.co/channels-and-providers/integration-store" + } + }, + { + "name": "Layouts", + "description": "Novu allows the creation of layouts - a specific HTML design or structure to wrap content of email notifications. Layouts can be manipulated and assigned to new or existing workflows within the Novu platform, allowing users to create, manage, and assign these layouts to workflows, so they can be reused to structure the appearance of notifications sent through the platform.", + "externalDocs": { + "url": "https://docs.novu.co/content-creation-design/layouts" + } + }, + { + "name": "Workflows", + "description": "All notifications are sent via a workflow. Each workflow acts as a container for the logic and blueprint that are associated with a type of notification in your system.", + "externalDocs": { "url": "https://docs.novu.co/workflows" } + }, + { + "name": "Notification Templates", + "description": "Deprecated. Use Workflows (/workflows) instead, which provide the same functionality under a new name." + }, + { + "name": "Workflow groups", + "description": "Workflow groups are used to organize workflows into logical groups." + }, + { + "name": "Changes", + "description": "Changes represent a change in state of an environment. They are analagous to a pending pull request in git, enabling you to test changes before they are applied to your environment and atomically apply them when you are ready.", + "externalDocs": { + "url": "https://docs.novu.co/platform/environments#promoting-pending-changes-to-production" + } + }, + { + "name": "Environments", + "description": "Novu uses the concept of environments to ensure logical separation of your data and configuration. This means that subscribers, and preferences created in one environment are never accessible to another.", + "externalDocs": { "url": "https://docs.novu.co/platform/environments" } + }, + { + "name": "Inbound Parse", + "description": "Inbound Webhook is a feature that allows processing of incoming emails for a domain or subdomain. The feature parses the contents of the email and POSTs the information to a specified URL in a multipart/form-data format.", + "externalDocs": { + "url": "https://docs.novu.co/platform/inbound-parse-webhook" + } + }, + { + "name": "Feeds", + "description": "Novu provides a notification activity feed that monitors every outgoing message associated with its relevant metadata. This can be used to monitor activity and discover potential issues with a specific provider or a channel type.", + "externalDocs": { "url": "https://docs.novu.co/activity-feed" } + }, + { + "name": "Tenants", + "description": "A tenant represents a group of users. As a developer, when your apps have organizations, they are referred to as tenants. Tenants in Novu provides the ability to tailor specific notification experiences to users of different groups or organizations.", + "externalDocs": { "url": "https://docs.novu.co/tenants" } + }, + { + "name": "Messages", + "description": "A message in Novu represents a notification delivered to a recipient on a particular channel. Messages contain information about the request that triggered its delivery, a view of the data sent to the recipient, and a timeline of its lifecycle events. Learn more about messages.", + "externalDocs": { "url": "https://docs.novu.co/workflows/messages" } + }, + { + "name": "Organizations", + "description": "An organization serves as a separate entity within your Novu account. Each organization you create has its own separate integration store, workflows, subscribers, and API keys. This separation of resources allows you to manage multi-tenant environments and separate domains within a single account.", + "externalDocs": { "url": "https://docs.novu.co/platform/organizations" } + }, + { + "name": "Execution Details", + "description": "Execution details are used to track the execution of a workflow. They provided detailed information on the execution of a workflow, including the status of each step, the input and output of each step, and the overall status of the execution.", + "externalDocs": { "url": "https://docs.novu.co/activity-feed" } + } + ], + "servers": [ + { "url": "https://api.novu.co" }, + { "url": "https://eu.api.novu.co" } + ], + "components": { + "securitySchemes": { + "api-key": { + "type": "apiKey", + "in": "header", + "name": "Authorization", + "description": "API key authentication. Allowed headers-- \"Authorization: ApiKey \"." + } + }, + "schemas": { + "DataWrapperDto": { + "type": "object", + "properties": { "data": { "type": "object" } }, + "required": ["data"] + }, + "OrganizationBrandingResponseDto": { + "type": "object", + "properties": { + "direction": { "enum": ["ltr", "trl"], "type": "string" }, + "logo": { "type": "string" }, + "color": { "type": "string" }, + "fontColor": { "type": "string" }, + "contentBackground": { "type": "string" }, + "fontFamily": { "type": "string" } + }, + "required": ["logo", "color", "fontColor", "contentBackground"] + }, + "IPartnerConfigurationResponseDto": { + "type": "object", + "properties": { + "projectIds": { "type": "array", "items": { "type": "string" } }, + "accessToken": { "type": "string" }, + "configurationId": { "type": "string" }, + "teamId": { "type": "string" }, + "partnerType": { + "type": "string", + "enum": ["vercel"], + "description": "Partner Type Enum" + } + }, + "required": ["accessToken", "configurationId", "partnerType"] + }, + "OrganizationResponseDto": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "logo": { "type": "string" }, + "branding": { + "$ref": "#/components/schemas/OrganizationBrandingResponseDto" + }, + "partnerConfigurations": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IPartnerConfigurationResponseDto" + } + } + }, + "required": ["name", "branding"] + }, + "CreateOrganizationDto": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "logo": { "type": "string" }, + "jobTitle": { + "enum": [ + "engineer", + "engineering_manager", + "architect", + "product_manager", + "designer", + "cxo_founder", + "marketing_manager", + "other" + ], + "type": "string" + }, + "domain": { "type": "string" }, + "productUseCases": { "type": "object" } + }, + "required": ["name"] + }, + "MemberUserDto": { + "type": "object", + "properties": { + "_id": { "type": "string" }, + "firstName": { "type": "string" }, + "lastName": { "type": "string" }, + "email": { "type": "string" } + }, + "required": ["_id", "firstName", "lastName", "email"] + }, + "MemberInviteDTO": { + "type": "object", + "properties": { + "email": { "type": "string" }, + "token": { "type": "string" }, + "invitationDate": { "format": "date-time", "type": "string" }, + "answerDate": { "format": "date-time", "type": "string" }, + "_inviterId": { "type": "string" } + }, + "required": ["email", "token", "invitationDate", "_inviterId"] + }, + "MemberResponseDto": { + "type": "object", + "properties": { + "_id": { "type": "string" }, + "_userId": { "type": "string" }, + "user": { "$ref": "#/components/schemas/MemberUserDto" }, + "roles": { "enum": ["admin", "member"], "type": "string" }, + "invite": { "$ref": "#/components/schemas/MemberInviteDTO" }, + "memberStatus": { + "enum": ["new", "active", "invited"], + "type": "string" + }, + "_organizationId": { "type": "string" } + }, + "required": ["_id", "_userId", "_organizationId"] + }, + "UpdateBrandingDetailsDto": { + "type": "object", + "properties": { + "logo": { "type": "string" }, + "color": { "type": "string" }, + "fontColor": { "type": "string" }, + "contentBackground": { "type": "string" }, + "fontFamily": { "type": "string" } + }, + "required": ["logo", "color", "fontColor", "contentBackground"] + }, + "RenameOrganizationDto": { + "type": "object", + "properties": { "name": { "type": "string" } }, + "required": ["name"] + }, + "EnvironmentResponseDto": { + "type": "object", + "properties": { + "_id": { "type": "string" }, + "name": { "type": "string" }, + "_organizationId": { "type": "string" }, + "identifier": { "type": "string" }, + "apiKeys": { "type": "array", "items": { "type": "object" } }, + "_parentId": { "type": "string" } + }, + "required": ["name", "_organizationId", "identifier", "_parentId"] + }, + "ApiKey": { + "type": "object", + "properties": { + "key": { "type": "string" }, + "_userId": { "type": "string" } + }, + "required": ["key", "_userId"] + }, + "ExecutionDetailsResponseDto": { + "type": "object", + "properties": { + "_id": { "type": "string" }, + "_organizationId": { "type": "string" }, + "_jobId": { "type": "string" }, + "_environmentId": { "type": "string" }, + "_notificationId": { "type": "string" }, + "_notificationTemplateId": { "type": "string" }, + "_subscriberId": { "type": "string" }, + "_messageId": { "type": "string" }, + "providerId": { "type": "string" }, + "transactionId": { "type": "string" }, + "channel": { + "type": "string", + "enum": [ + "in_app", + "email", + "sms", + "chat", + "push", + "digest", + "trigger", + "delay", + "custom" + ] + }, + "detail": { "type": "string" }, + "source": { + "type": "string", + "enum": ["Credentials", "Internal", "Payload", "Webhook"] + }, + "status": { + "type": "string", + "enum": [ + "Success", + "Warning", + "Failed", + "Pending", + "Queued", + "ReadConfirmation" + ] + }, + "isTest": { "type": "boolean" }, + "isRetry": { "type": "boolean" }, + "createdAt": { "type": "string" } + }, + "required": [ + "_organizationId", + "_jobId", + "_environmentId", + "_notificationId", + "_notificationTemplateId", + "_subscriberId", + "transactionId", + "channel", + "detail", + "source", + "status", + "isTest", + "isRetry" + ] + }, + "NotificationGroup": { + "type": "object", + "properties": { + "_id": { "type": "string" }, + "name": { "type": "string" }, + "_environmentId": { "type": "string" }, + "_organizationId": { "type": "string" }, + "_parentId": { "type": "string" } + }, + "required": ["name", "_environmentId", "_organizationId"] + }, + "PreferenceChannels": { + "type": "object", + "properties": { + "email": { "type": "boolean" }, + "sms": { "type": "boolean" }, + "in_app": { "type": "boolean" }, + "chat": { "type": "boolean" }, + "push": { "type": "boolean" } + } + }, + "DigestRegularMetadata": { + "type": "object", + "properties": { + "amount": { "type": "number" }, + "unit": { + "type": "string", + "enum": ["seconds", "minutes", "hours", "days", "weeks", "months"] + }, + "digestKey": { "type": "string" }, + "type": { "type": "string", "enum": ["regular", "backoff"] }, + "backoff": { "type": "boolean" }, + "backoffAmount": { "type": "number" }, + "backoffUnit": { + "type": "string", + "enum": ["seconds", "minutes", "hours", "days", "weeks", "months"] + }, + "updateMode": { "type": "boolean" } + }, + "required": ["type"] + }, + "TimedConfig": { + "type": "object", + "properties": { + "atTime": { "type": "string" }, + "weekDays": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "monday", + "tuesday", + "wednesday", + "thursday", + "friday", + "saturday", + "sunday" + ] + } + }, + "monthDays": { "type": "array", "items": { "type": "string" } }, + "ordinal": { + "type": "string", + "enum": ["1", "2", "3", "4", "5", "last"] + }, + "ordinalValue": { + "type": "string", + "enum": [ + "day", + "weekday", + "weekend", + "sunday", + "monday", + "tuesday", + "wednesday", + "thursday", + "friday", + "saturday" + ] + }, + "monthlyType": { "type": "string", "enum": ["each", "on"] } + } + }, + "DigestTimedMetadata": { + "type": "object", + "properties": { + "amount": { "type": "number" }, + "unit": { + "type": "string", + "enum": ["seconds", "minutes", "hours", "days", "weeks", "months"] + }, + "digestKey": { "type": "string" }, + "type": { "type": "string", "enum": ["timed"] }, + "timed": { "$ref": "#/components/schemas/TimedConfig" } + }, + "required": ["type"] + }, + "DelayRegularMetadata": { + "type": "object", + "properties": { + "amount": { "type": "number" }, + "unit": { + "type": "string", + "enum": ["seconds", "minutes", "hours", "days", "weeks", "months"] + }, + "type": { "type": "string", "enum": ["regular"] } + }, + "required": ["type"] + }, + "DelayScheduledMetadata": { + "type": "object", + "properties": { + "type": { "type": "string", "enum": ["scheduled"] }, + "delayPath": { "type": "string" } + }, + "required": ["type", "delayPath"] + }, + "MessageTemplate": { "type": "object", "properties": {} }, + "FieldFilterPart": { + "type": "object", + "properties": { + "field": { "type": "string" }, + "value": { "type": "string" }, + "operator": { + "type": "string", + "enum": [ + "LARGER", + "SMALLER", + "LARGER_EQUAL", + "SMALLER_EQUAL", + "EQUAL", + "NOT_EQUAL", + "ALL_IN", + "ANY_IN", + "NOT_IN", + "BETWEEN", + "NOT_BETWEEN", + "LIKE", + "NOT_LIKE", + "IN" + ] + }, + "on": { "type": "string", "enum": ["subscriber", "payload"] } + }, + "required": ["field", "value", "operator", "on"] + }, + "StepFilter": { + "type": "object", + "properties": { + "isNegated": { "type": "boolean" }, + "type": { + "type": "string", + "enum": [ + "BOOLEAN", + "TEXT", + "DATE", + "NUMBER", + "STATEMENT", + "LIST", + "MULTI_LIST", + "GROUP" + ] + }, + "value": { "type": "string", "enum": ["AND", "OR"] }, + "children": { + "type": "array", + "items": { "$ref": "#/components/schemas/FieldFilterPart" } + } + }, + "required": ["isNegated", "type", "value", "children"] + }, + "NotificationStepVariant": { + "type": "object", + "properties": { + "_id": { "type": "string" }, + "uuid": { "type": "string" }, + "name": { "type": "string" }, + "_templateId": { "type": "string" }, + "active": { "type": "boolean" }, + "shouldStopOnFail": { "type": "boolean" }, + "template": { "$ref": "#/components/schemas/MessageTemplate" }, + "filters": { + "type": "array", + "items": { "$ref": "#/components/schemas/StepFilter" } + }, + "_parentId": { "type": "object" }, + "metadata": { + "oneOf": [ + { "$ref": "#/components/schemas/DigestRegularMetadata" }, + { "$ref": "#/components/schemas/DigestTimedMetadata" }, + { "$ref": "#/components/schemas/DelayRegularMetadata" }, + { "$ref": "#/components/schemas/DelayScheduledMetadata" } + ] + }, + "replyCallback": { "type": "object" } + } + }, + "NotificationStep": { + "type": "object", + "properties": { + "_id": { "type": "string" }, + "uuid": { "type": "string" }, + "name": { "type": "string" }, + "_templateId": { "type": "string" }, + "active": { "type": "boolean" }, + "shouldStopOnFail": { "type": "boolean" }, + "template": { "$ref": "#/components/schemas/MessageTemplate" }, + "filters": { + "type": "array", + "items": { "$ref": "#/components/schemas/StepFilter" } + }, + "_parentId": { "type": "object" }, + "metadata": { + "oneOf": [ + { "$ref": "#/components/schemas/DigestRegularMetadata" }, + { "$ref": "#/components/schemas/DigestTimedMetadata" }, + { "$ref": "#/components/schemas/DelayRegularMetadata" }, + { "$ref": "#/components/schemas/DelayScheduledMetadata" } + ] + }, + "replyCallback": { "type": "object" }, + "variants": { "$ref": "#/components/schemas/NotificationStepVariant" } + } + }, + "NotificationTriggerVariable": { + "type": "object", + "properties": { "name": { "type": "string" } }, + "required": ["name"] + }, + "NotificationTrigger": { + "type": "object", + "properties": { + "type": { "type": "string", "enum": ["event"] }, + "identifier": { "type": "string" }, + "variables": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NotificationTriggerVariable" + } + }, + "subscriberVariables": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NotificationTriggerVariable" + } + } + }, + "required": ["type", "identifier", "variables"] + }, + "WorkflowResponse": { + "type": "object", + "properties": { + "_id": { "type": "string" }, + "name": { "type": "string" }, + "description": { "type": "string" }, + "active": { "type": "boolean" }, + "draft": { "type": "boolean" }, + "preferenceSettings": { + "$ref": "#/components/schemas/PreferenceChannels" + }, + "critical": { "type": "boolean" }, + "tags": { "type": "array", "items": { "type": "string" } }, + "steps": { + "type": "array", + "items": { "$ref": "#/components/schemas/NotificationStep" } + }, + "_organizationId": { "type": "string" }, + "_creatorId": { "type": "string" }, + "_environmentId": { "type": "string" }, + "triggers": { + "type": "array", + "items": { "$ref": "#/components/schemas/NotificationTrigger" } + }, + "_notificationGroupId": { "type": "string" }, + "_parentId": { "type": "string" }, + "deleted": { "type": "boolean" }, + "deletedAt": { "type": "string" }, + "deletedBy": { "type": "string" }, + "notificationGroup": { + "$ref": "#/components/schemas/NotificationGroup" + }, + "data": { "type": "object" }, + "workflowIntegrationStatus": { "type": "object" } + }, + "required": [ + "name", + "description", + "active", + "draft", + "preferenceSettings", + "critical", + "tags", + "steps", + "_organizationId", + "_creatorId", + "_environmentId", + "triggers", + "_notificationGroupId", + "deleted", + "deletedAt", + "deletedBy" + ] + }, + "WorkflowsResponseDto": { + "type": "object", + "properties": { + "totalCount": { "type": "number" }, + "data": { + "type": "array", + "items": { "$ref": "#/components/schemas/WorkflowResponse" } + }, + "pageSize": { "type": "number" }, + "page": { "type": "number" } + }, + "required": ["totalCount", "data", "pageSize", "page"] + }, + "UpdateWorkflowRequestDto": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "tags": { "type": "array", "items": { "type": "string" } }, + "description": { "type": "string", "maxLength": 300 }, + "identifier": { "type": "string" }, + "steps": { + "type": "array", + "items": { "$ref": "#/components/schemas/NotificationStep" } + }, + "notificationGroupId": { "type": "string" }, + "critical": { "type": "boolean" }, + "preferenceSettings": { + "$ref": "#/components/schemas/PreferenceChannels" + }, + "data": { "type": "object" } + }, + "required": ["name", "notificationGroupId"] + }, + "DataBooleanDto": { + "type": "object", + "properties": { "data": { "type": "boolean" } }, + "required": ["data"] + }, + "VariablesResponseDto": { + "type": "object", + "properties": { + "translations": { "type": "object" }, + "system": { "type": "object" } + }, + "required": ["translations", "system"] + }, + "CreateWorkflowRequestDto": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "notificationGroupId": { "type": "string" }, + "notificationGroup": { "type": "object" }, + "tags": { "type": "array", "items": { "type": "string" } }, + "description": { "type": "string", "maxLength": 1000 }, + "steps": { + "type": "array", + "items": { "$ref": "#/components/schemas/NotificationStep" } + }, + "active": { "type": "boolean" }, + "draft": { "type": "boolean", "deprecated": true }, + "critical": { "type": "boolean" }, + "preferenceSettings": { + "$ref": "#/components/schemas/PreferenceChannels" + }, + "blueprintId": { "type": "string" }, + "data": { "type": "object" } + }, + "required": ["name", "notificationGroupId", "steps"] + }, + "ChangeWorkflowStatusRequestDto": { + "type": "object", + "properties": { "active": { "type": "boolean" } }, + "required": ["active"] + }, + "TriggerEventResponseDto": { + "type": "object", + "properties": { + "acknowledged": { + "type": "boolean", + "description": "If trigger was acknowledged or not" + }, + "status": { + "enum": [ + "error", + "trigger_not_active", + "no_workflow_active_steps_defined", + "no_workflow_steps_defined", + "processed", + "subscriber_id_missing", + "no_tenant_found" + ], + "type": "string", + "description": "Status for trigger" + }, + "error": { + "description": "In case of an error, this field will contain the error message", + "type": "array", + "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction id for trigger" + } + }, + "required": ["acknowledged", "status"] + }, + "TopicPayloadDto": { + "type": "object", + "properties": { + "topicKey": { "type": "string", "example": "topic_key" }, + "type": { + "enum": ["Subscriber", "Topic"], + "type": "string", + "example": "Topic" + } + }, + "required": ["topicKey", "type"] + }, + "TenantPayloadDto": { + "type": "object", + "properties": { + "identifier": { "type": "string" }, + "name": { "type": "string" }, + "data": { "type": "object" } + } + }, + "ChannelCredentialsDto": { + "type": "object", + "properties": { + "webhookUrl": { "type": "string" }, + "deviceTokens": { "type": "array", "items": { "type": "string" } } + } + }, + "SubscriberChannelDto": { + "type": "object", + "properties": { + "integrationIdentifier": { "type": "string" }, + "providerId": { "type": "object" }, + "credentials": { + "$ref": "#/components/schemas/ChannelCredentialsDto" + } + }, + "required": ["providerId", "credentials"] + }, + "SubscriberPayloadDto": { + "type": "object", + "properties": { + "subscriberId": { + "type": "string", + "description": "The internal identifier you used to create this subscriber, usually correlates to the id the user in your systems" + }, + "email": { "type": "string" }, + "firstName": { "type": "string" }, + "lastName": { "type": "string" }, + "phone": { "type": "string" }, + "avatar": { + "type": "string", + "description": "An http url to the profile image of your subscriber" + }, + "locale": { "type": "string" }, + "data": { "type": "object" }, + "channels": { + "type": "array", + "items": { "$ref": "#/components/schemas/SubscriberChannelDto" } + } + }, + "required": ["subscriberId"] + }, + "TriggerEventRequestDto": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The trigger identifier of the workflow you wish to send. This identifier can be found on the workflow page.", + "example": "workflow_identifier" + }, + "payload": { + "type": "object", + "description": "The payload object is used to pass additional custom information that could be used to render the workflow, or perform routing rules based on it. \n This data will also be available when fetching the notifications feed from the API to display certain parts of the UI.", + "example": { "comment_id": "string", "post": { "text": "string" } } + }, + "overrides": { + "type": "object", + "description": "This could be used to override provider specific configurations", + "example": { "fcm": { "data": { "key": "value" } } } + }, + "to": { + "type": "array", + "description": "The recipients list of people who will receive the notification.", + "items": { + "oneOf": [ + { "$ref": "#/components/schemas/SubscriberPayloadDto" }, + { + "type": "string", + "description": "Unique identifier of a subscriber in your systems", + "example": "SUBSCRIBER_ID" + }, + { "$ref": "#/components/schemas/TopicPayloadDto" } + ] + } + }, + "transactionId": { + "type": "string", + "description": "A unique identifier for this transaction, we will generated a UUID if not provided." + }, + "actor": { + "description": "It is used to display the Avatar of the provided actor's subscriber id or actor object.\n If a new actor object is provided, we will create a new subscriber in our system\n ", + "oneOf": [ + { + "type": "string", + "description": "Unique identifier of a subscriber in your systems" + }, + { "$ref": "#/components/schemas/SubscriberPayloadDto" } + ] + }, + "tenant": { + "description": "It is used to specify a tenant context during trigger event.\n Existing tenants will be updated with the provided details.\n ", + "oneOf": [ + { + "type": "string", + "description": "Unique identifier of a tenant in your system" + }, + { "$ref": "#/components/schemas/TenantPayloadDto" } + ] + } + }, + "required": ["name", "to"] + }, + "BulkTriggerEventDto": { + "type": "object", + "properties": { + "events": { + "type": "array", + "items": { "$ref": "#/components/schemas/TriggerEventRequestDto" } + } + }, + "required": ["events"] + }, + "TriggerEventToAllRequestDto": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The trigger identifier associated for the template you wish to send. This identifier can be found on the template page." + }, + "payload": { + "type": "object", + "description": "The payload object is used to pass additional custom information that could be used to render the template, or perform routing rules based on it. \n This data will also be available when fetching the notifications feed from the API to display certain parts of the UI.", + "example": { "comment_id": "string", "post": { "text": "string" } } + }, + "overrides": { + "type": "object", + "description": "This could be used to override provider specific configurations", + "example": { "fcm": { "data": { "key": "value" } } } + }, + "transactionId": { + "type": "string", + "description": "A unique identifier for this transaction, we will generated a UUID if not provided." + }, + "actor": { + "description": "It is used to display the Avatar of the provided actor's subscriber id or actor object.\n If a new actor object is provided, we will create a new subscriber in our system\n ", + "oneOf": [ + { + "type": "string", + "description": "Unique identifier of a subscriber in your systems" + }, + { "$ref": "#/components/schemas/SubscriberPayloadDto" } + ] + }, + "tenant": { + "description": "It is used to specify a tenant context during trigger event.\n If a new tenant object is provided, we will create a new tenant.\n ", + "oneOf": [ + { + "type": "string", + "description": "Unique identifier of a tenant in your system" + }, + { "$ref": "#/components/schemas/TenantPayloadDto" } + ] + } + }, + "required": ["name", "payload"] + }, + "ActivityNotificationSubscriberResponseDto": { + "type": "object", + "properties": { + "firstName": { "type": "string" }, + "_id": { "type": "string" }, + "lastName": { "type": "string" }, + "email": { "type": "string" }, + "phone": { "type": "string" } + }, + "required": ["_id"] + }, + "ActivityNotificationTemplateResponseDto": { + "type": "object", + "properties": { + "_id": { "type": "string" }, + "name": { "type": "string" }, + "triggers": { + "type": "array", + "items": { "$ref": "#/components/schemas/NotificationTrigger" } + } + }, + "required": ["name", "triggers"] + }, + "ActivityNotificationExecutionDetailResponseDto": { + "type": "object", + "properties": { + "_id": { "type": "string" }, + "_jobId": { "type": "string" }, + "status": { + "enum": [ + "Success", + "Warning", + "Failed", + "Pending", + "Queued", + "ReadConfirmation" + ], + "type": "string" + }, + "detail": { "type": "string" }, + "isRetry": { "type": "boolean" }, + "isTest": { "type": "boolean" }, + "providerId": { "type": "object" }, + "raw": { "type": "string" }, + "source": { + "enum": ["Credentials", "Internal", "Payload", "Webhook"], + "type": "string" + } + }, + "required": [ + "_id", + "_jobId", + "status", + "detail", + "isRetry", + "isTest", + "providerId", + "source" + ] + }, + "MessageTemplateDto": { "type": "object", "properties": {} }, + "ActivityNotificationStepResponseDto": { + "type": "object", + "properties": { + "_id": { "type": "string" }, + "active": { "type": "boolean" }, + "filters": { "$ref": "#/components/schemas/StepFilter" }, + "template": { "$ref": "#/components/schemas/MessageTemplateDto" } + }, + "required": ["_id", "active", "filters"] + }, + "ActivityNotificationJobResponseDto": { + "type": "object", + "properties": { + "_id": { "type": "string" }, + "type": { "type": "string" }, + "digest": { "type": "object" }, + "executionDetails": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ActivityNotificationExecutionDetailResponseDto" + } + }, + "step": { + "$ref": "#/components/schemas/ActivityNotificationStepResponseDto" + }, + "payload": { "type": "object" }, + "providerId": { "type": "object" }, + "status": { "type": "string" } + }, + "required": [ + "_id", + "type", + "executionDetails", + "step", + "providerId", + "status" + ] + }, + "ActivityNotificationResponseDto": { + "type": "object", + "properties": { + "_id": { "type": "string" }, + "_environmentId": { "type": "string" }, + "_organizationId": { "type": "string" }, + "transactionId": { "type": "string" }, + "createdAt": { "type": "string" }, + "channels": { + "type": "string", + "items": { + "type": "string", + "enum": [ + "in_app", + "email", + "sms", + "chat", + "push", + "digest", + "trigger", + "delay", + "custom" + ] + }, + "enum": [ + "in_app", + "email", + "sms", + "chat", + "push", + "digest", + "trigger", + "delay", + "custom" + ] + }, + "subscriber": { + "$ref": "#/components/schemas/ActivityNotificationSubscriberResponseDto" + }, + "template": { + "$ref": "#/components/schemas/ActivityNotificationTemplateResponseDto" + }, + "jobs": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ActivityNotificationJobResponseDto" + } + } + }, + "required": ["_environmentId", "_organizationId", "transactionId"] + }, + "ActivitiesResponseDto": { + "type": "object", + "properties": { + "hasMore": { "type": "boolean" }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ActivityNotificationResponseDto" + } + }, + "pageSize": { "type": "number" }, + "page": { "type": "number" } + }, + "required": ["hasMore", "data", "pageSize", "page"] + }, + "ActivityStatsResponseDto": { + "type": "object", + "properties": { + "weeklySent": { "type": "number" }, + "monthlySent": { "type": "number" } + }, + "required": ["weeklySent", "monthlySent"] + }, + "ActivityGraphStatesResponse": { + "type": "object", + "properties": { + "_id": { "type": "string" }, + "count": { "type": "number" }, + "templates": { "type": "array", "items": { "type": "string" } }, + "channels": { + "type": "array", + "items": { + "type": "string", + "enum": ["in_app", "email", "sms", "chat", "push"] + } + } + }, + "required": ["_id", "count", "templates", "channels"] + }, + "NotificationGroupResponseDto": { + "type": "object", + "properties": { + "_id": { "type": "string" }, + "name": { "type": "string" }, + "_environmentId": { "type": "string" }, + "_organizationId": { "type": "string" }, + "_parentId": { "type": "string" } + }, + "required": ["name", "_environmentId", "_organizationId"] + }, + "CreateNotificationGroupRequestDto": { + "type": "object", + "properties": { "name": { "type": "string" } }, + "required": ["name"] + }, + "DeleteNotificationGroupResponseDto": { + "type": "object", + "properties": { + "acknowledged": { + "type": "boolean", + "description": "A boolean stating the success of the action" + }, + "status": { + "type": "string", + "description": "The status enum for the performed action", + "enum": ["deleted"] + } + }, + "required": ["acknowledged", "status"] + }, + "CredentialsDto": { + "type": "object", + "properties": { + "apiKey": { "type": "string" }, + "user": { "type": "string" }, + "secretKey": { "type": "string" }, + "domain": { "type": "string" }, + "password": { "type": "string" }, + "host": { "type": "string" }, + "port": { "type": "string" }, + "secure": { "type": "boolean" }, + "region": { "type": "string" }, + "accountSid": { "type": "string" }, + "messageProfileId": { "type": "string" }, + "token": { "type": "string" }, + "from": { "type": "string" }, + "senderName": { "type": "string" }, + "projectName": { "type": "string" }, + "applicationId": { "type": "string" }, + "clientId": { "type": "string" }, + "requireTls": { "type": "boolean" }, + "ignoreTls": { "type": "boolean" }, + "tlsOptions": { "type": "object" }, + "baseUrl": { "type": "string" }, + "webhookUrl": { "type": "string" }, + "redirectUrl": { "type": "string" }, + "hmac": { "type": "boolean" }, + "serviceAccount": { "type": "string" }, + "ipPoolName": { "type": "string" }, + "apiKeyRequestHeader": { "type": "string" }, + "secretKeyRequestHeader": { "type": "string" }, + "idPath": { "type": "string" }, + "datePath": { "type": "string" }, + "apiToken": { "type": "string" }, + "authenticateByToken": { "type": "boolean" }, + "authenticationTokenKey": { "type": "string" }, + "instanceId": { "type": "string" }, + "alertUid": { "type": "string" }, + "title": { "type": "string" }, + "imageUrl": { "type": "string" }, + "state": { "type": "string" }, + "externalLink": { "type": "string" }, + "channelId": { "type": "string" }, + "phoneNumberIdentification": { "type": "string" } + } + }, + "IntegrationResponseDto": { + "type": "object", + "properties": { + "_id": { "type": "string" }, + "_environmentId": { "type": "string" }, + "_organizationId": { "type": "string" }, + "name": { "type": "string" }, + "identifier": { "type": "string" }, + "providerId": { "type": "string" }, + "channel": { + "enum": ["in_app", "email", "sms", "chat", "push"], + "type": "string" + }, + "credentials": { "$ref": "#/components/schemas/CredentialsDto" }, + "active": { "type": "boolean" }, + "deleted": { "type": "boolean" }, + "deletedAt": { "type": "string" }, + "deletedBy": { "type": "string" }, + "primary": { "type": "boolean" }, + "conditions": { + "type": "array", + "items": { "$ref": "#/components/schemas/StepFilter" } + } + }, + "required": [ + "_environmentId", + "_organizationId", + "name", + "identifier", + "providerId", + "channel", + "credentials", + "active", + "deleted", + "deletedAt", + "deletedBy", + "primary" + ] + }, + "CreateIntegrationRequestDto": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "identifier": { "type": "string" }, + "_environmentId": { "type": "string" }, + "providerId": { "type": "string" }, + "channel": { + "enum": ["in_app", "email", "sms", "chat", "push"], + "type": "string" + }, + "credentials": { "$ref": "#/components/schemas/CredentialsDto" }, + "active": { + "type": "boolean", + "description": "If the integration is active the validation on the credentials field will run" + }, + "check": { "type": "boolean" }, + "conditions": { + "type": "array", + "items": { "$ref": "#/components/schemas/StepFilter" } + } + }, + "required": ["providerId", "channel"] + }, + "UpdateIntegrationRequestDto": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "identifier": { "type": "string" }, + "_environmentId": { "type": "string" }, + "active": { + "type": "boolean", + "description": "If the integration is active the validation on the credentials field will run" + }, + "credentials": { "$ref": "#/components/schemas/CredentialsDto" }, + "check": { "type": "boolean" }, + "conditions": { + "type": "array", + "items": { "$ref": "#/components/schemas/StepFilter" } + } + } + }, + "ChangeResponseDto": { + "type": "object", + "properties": { + "_id": { "type": "string" }, + "_creatorId": { "type": "string" }, + "_environmentId": { "type": "string" }, + "_organizationId": { "type": "string" }, + "_entityId": { "type": "string" }, + "enabled": { "type": "boolean" }, + "type": { + "enum": [ + "Feed", + "MessageTemplate", + "Layout", + "DefaultLayout", + "NotificationTemplate", + "NotificationGroup", + "TranslationGroup", + "Translation" + ], + "type": "string" + }, + "change": { "type": "object" }, + "createdAt": { "type": "string" }, + "_parentId": { "type": "string" } + }, + "required": [ + "_creatorId", + "_environmentId", + "_organizationId", + "_entityId", + "enabled", + "type", + "change", + "createdAt" + ] + }, + "ChangesResponseDto": { + "type": "object", + "properties": { + "totalCount": { "type": "number" }, + "data": { + "type": "array", + "items": { "$ref": "#/components/schemas/ChangeResponseDto" } + }, + "pageSize": { "type": "number" }, + "page": { "type": "number" } + }, + "required": ["totalCount", "data", "pageSize", "page"] + }, + "DataNumberDto": { + "type": "object", + "properties": { "data": { "type": "number" } }, + "required": ["data"] + }, + "BulkApplyChangeDto": { + "type": "object", + "properties": { + "changeIds": { "type": "array", "items": { "type": "string" } } + }, + "required": ["changeIds"] + }, + "PaginatedResponseDto": { + "type": "object", + "properties": { + "page": { + "type": "number", + "description": "The current page of the paginated response" + }, + "hasMore": { + "type": "boolean", + "description": "Does the list have more items to fetch" + }, + "pageSize": { + "type": "number", + "description": "Number of items on each page" + }, + "data": { + "description": "The list of items matching the query", + "type": "array", + "items": { "type": "object" } + } + }, + "required": ["page", "hasMore", "pageSize", "data"] + }, + "ChannelCredentials": { + "type": "object", + "properties": { + "webhookUrl": { + "type": "string", + "description": "Webhook url used by chat app integrations. The webhook should be obtained from the chat app provider." + }, + "channel": { + "type": "string", + "description": "Channel specification for Mattermost chat notifications" + }, + "deviceTokens": { + "description": "Contains an array of the subscriber device tokens for a given provider. Used on Push integrations", + "type": "array", + "items": { "type": "string" } + }, + "alertUid": { + "type": "string", + "description": "alert_uid for grafana on-call webhook payload" + }, + "title": { + "type": "string", + "description": "title to be used with grafana on call webhook" + }, + "imageUrl": { + "type": "string", + "description": "image_url property fo grafana on call webhook" + }, + "state": { + "type": "string", + "description": "state property fo grafana on call webhook" + }, + "externalUrl": { + "type": "string", + "description": "link_to_upstream_details property fo grafana on call webhook" + } + }, + "required": ["webhookUrl"] + }, + "ChannelSettings": { + "type": "object", + "properties": { + "providerId": { + "type": "string", + "enum": [ + "slack", + "discord", + "msteams", + "mattermost", + "ryver", + "zulip", + "grafana-on-call", + "getstream", + "rocket-chat", + "whatsapp-business", + "fcm", + "apns", + "expo", + "one-signal", + "pushpad", + "push-webhook", + "pusher-beams" + ], + "description": "The provider identifier for the credentials" + }, + "integrationIdentifier": { + "type": "string", + "description": "The integration identifier" + }, + "credentials": { + "description": "Credentials payload for the specified provider", + "allOf": [{ "$ref": "#/components/schemas/ChannelCredentials" }] + }, + "_integrationId": { + "type": "string", + "description": "Id of the integration that is used for this channel" + } + }, + "required": ["providerId", "credentials", "_integrationId"] + }, + "SubscriberResponseDto": { + "type": "object", + "properties": { + "_id": { + "type": "string", + "description": "The internal id novu generated for your subscriber, this is not the subscriberId matching your query. See `subscriberId` for that" + }, + "firstName": { "type": "string" }, + "lastName": { "type": "string" }, + "email": { "type": "string" }, + "phone": { "type": "string" }, + "avatar": { "type": "string" }, + "locale": { "type": "string" }, + "subscriberId": { + "type": "string", + "description": "The internal identifier you used to create this subscriber, usually correlates to the id the user in your systems" + }, + "channels": { + "description": "Channels settings for subscriber", + "type": "array", + "items": { "$ref": "#/components/schemas/ChannelSettings" } + }, + "isOnline": { "type": "boolean" }, + "lastOnlineAt": { "type": "string" }, + "_organizationId": { "type": "string" }, + "_environmentId": { "type": "string" }, + "deleted": { "type": "boolean" }, + "createdAt": { "type": "string" }, + "updatedAt": { "type": "string" }, + "__v": { "type": "number" } + }, + "required": [ + "subscriberId", + "_organizationId", + "_environmentId", + "deleted", + "createdAt", + "updatedAt" + ] + }, + "CreateSubscriberRequestDto": { + "type": "object", + "properties": { + "subscriberId": { + "type": "string", + "description": "The internal identifier you used to create this subscriber, usually correlates to the id the user in your systems" + }, + "email": { "type": "string" }, + "firstName": { "type": "string" }, + "lastName": { "type": "string" }, + "phone": { "type": "string" }, + "avatar": { + "type": "string", + "description": "An http url to the profile image of your subscriber" + }, + "locale": { "type": "string" }, + "data": { "type": "object" }, + "channels": { + "type": "array", + "items": { "$ref": "#/components/schemas/SubscriberChannelDto" } + } + }, + "required": ["subscriberId"] + }, + "BulkSubscriberCreateDto": { + "type": "object", + "properties": { + "subscribers": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CreateSubscriberRequestDto" + } + } + }, + "required": ["subscribers"] + }, + "UpdateSubscriberRequestDto": { + "type": "object", + "properties": { + "email": { "type": "string" }, + "firstName": { "type": "string" }, + "lastName": { "type": "string" }, + "phone": { "type": "string" }, + "avatar": { "type": "string" }, + "locale": { "type": "string" }, + "data": { "type": "object" } + } + }, + "UpdateSubscriberChannelRequestDto": { + "type": "object", + "properties": { + "providerId": { + "type": "string", + "enum": [ + "slack", + "discord", + "msteams", + "mattermost", + "ryver", + "zulip", + "grafana-on-call", + "getstream", + "rocket-chat", + "whatsapp-business", + "fcm", + "apns", + "expo", + "one-signal", + "pushpad", + "push-webhook", + "pusher-beams" + ], + "description": "The provider identifier for the credentials" + }, + "integrationIdentifier": { + "type": "string", + "description": "The integration identifier" + }, + "credentials": { + "description": "Credentials payload for the specified provider", + "allOf": [{ "$ref": "#/components/schemas/ChannelCredentials" }] + } + }, + "required": ["providerId", "credentials"] + }, + "UpdateSubscriberOnlineFlagRequestDto": { + "type": "object", + "properties": { "isOnline": { "type": "boolean" } }, + "required": ["isOnline"] + }, + "DeleteSubscriberResponseDto": { + "type": "object", + "properties": { + "acknowledged": { + "type": "boolean", + "description": "A boolean stating the success of the action" + }, + "status": { + "type": "string", + "description": "The status enum for the performed action", + "enum": ["deleted"] + } + }, + "required": ["acknowledged", "status"] + }, + "TemplateResponse": { + "type": "object", + "properties": { + "_id": { + "type": "string", + "description": "Unique identifier of the workflow" + }, + "name": { "type": "string", "description": "Name of the workflow" }, + "critical": { + "type": "boolean", + "description": "Critical templates will always be delivered to the end user and should be hidden from the subscriber preferences screen" + }, + "triggers": { + "description": "Triggers are the events that will trigger the workflow.", + "type": "array", + "items": { "type": "string" } + } + }, + "required": ["_id", "name", "critical", "triggers"] + }, + "Preference": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Sets if the workflow is fully enabled for all channels or not for the subscriber." + }, + "channels": { + "description": "Subscriber preferences for the different channels regarding this workflow", + "allOf": [{ "$ref": "#/components/schemas/PreferenceChannels" }] + } + }, + "required": ["enabled", "channels"] + }, + "UpdateSubscriberPreferenceResponseDto": { + "type": "object", + "properties": { + "template": { + "description": "The workflow information and if it is critical or not", + "allOf": [{ "$ref": "#/components/schemas/TemplateResponse" }] + }, + "preference": { + "description": "The preferences of the subscriber regarding the related workflow", + "allOf": [{ "$ref": "#/components/schemas/Preference" }] + } + }, + "required": ["template", "preference"] + }, + "GetSubscriberPreferencesResponseDto": { + "type": "object", + "properties": { + "template": { + "description": "The workflow information and if it is critical or not", + "allOf": [{ "$ref": "#/components/schemas/TemplateResponse" }] + }, + "preference": { + "description": "The preferences of the subscriber regarding the related workflow", + "allOf": [{ "$ref": "#/components/schemas/Preference" }] + } + }, + "required": ["preference"] + }, + "ChannelPreference": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["in_app", "email", "sms", "chat", "push"], + "description": "The type of channel that is enabled or not" + }, + "enabled": { + "type": "boolean", + "description": "If channel is enabled or not" + } + }, + "required": ["type", "enabled"] + }, + "UpdateSubscriberPreferenceRequestDto": { + "type": "object", + "properties": { + "channel": { + "description": "The subscriber preferences for every ChannelTypeEnum for the workflow assigned.", + "allOf": [{ "$ref": "#/components/schemas/ChannelPreference" }] + }, + "enabled": { + "type": "boolean", + "description": "Sets if the workflow is fully enabled for all channels or not for the subscriber." + } + } + }, + "UpdateSubscriberGlobalPreferencesRequestDto": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable or disable the subscriber global preferences." + }, + "preferences": { + "description": "The subscriber global preferences for every ChannelTypeEnum.", + "type": "array", + "items": { "$ref": "#/components/schemas/ChannelPreference" } + } + } + }, + "EmailBlockStyles": { + "type": "object", + "properties": { + "textAlign": { "enum": ["left", "right", "center"], "type": "string" } + } + }, + "EmailBlock": { + "type": "object", + "properties": { + "type": { "enum": ["text", "button"], "type": "string" }, + "content": { "type": "string" }, + "url": { "type": "string" }, + "styles": { "$ref": "#/components/schemas/EmailBlockStyles" } + }, + "required": ["type", "content"] + }, + "MessageCTAData": { + "type": "object", + "properties": { "url": { "type": "string" } } + }, + "MessageButton": { + "type": "object", + "properties": { + "type": { + "enum": ["primary", "secondary", "clicked"], + "type": "string" + }, + "content": { "type": "string" }, + "resultContent": { "type": "string" } + }, + "required": ["type", "content"] + }, + "MessageActionResult": { + "type": "object", + "properties": { + "payload": { "type": "object" }, + "type": { + "enum": ["primary", "secondary", "clicked"], + "type": "string" + } + } + }, + "MessageAction": { + "type": "object", + "properties": { + "status": { "enum": ["pending", "done"], "type": "string" }, + "buttons": { + "type": "array", + "items": { "$ref": "#/components/schemas/MessageButton" } + }, + "result": { "$ref": "#/components/schemas/MessageActionResult" } + } + }, + "MessageCTA": { + "type": "object", + "properties": { + "type": { "type": "string", "enum": ["redirect"] }, + "data": { "$ref": "#/components/schemas/MessageCTAData" }, + "action": { "$ref": "#/components/schemas/MessageAction" } + }, + "required": ["data"] + }, + "Actor": { + "type": "object", + "properties": { + "data": { "type": "string", "nullable": true }, + "type": { + "enum": ["none", "user", "system_icon", "system_custom"], + "type": "string" + } + }, + "required": ["data", "type"] + }, + "NotificationDto": { + "type": "object", + "properties": { + "_id": { "type": "string" }, + "_templateId": { "type": "string" }, + "_environmentId": { "type": "string" }, + "_messageTemplateId": { "type": "string" }, + "_organizationId": { "type": "string" }, + "_notificationId": { "type": "string" }, + "_subscriberId": { "type": "string" }, + "_feedId": { "type": "string" }, + "_jobId": { "type": "string" }, + "createdAt": { "type": "string" }, + "updatedAt": { "type": "string" }, + "expireAt": { "type": "string" }, + "actor": { "$ref": "#/components/schemas/Actor" }, + "subscriber": { + "$ref": "#/components/schemas/SubscriberResponseDto" + }, + "transactionId": { "type": "string" }, + "templateIdentifier": { "type": "string" }, + "providerId": { "type": "string" }, + "content": { "type": "string" }, + "subject": { "type": "string" }, + "channel": { + "enum": ["in_app", "email", "sms", "chat", "push"], + "type": "string" + }, + "read": { "type": "boolean" }, + "seen": { "type": "boolean" }, + "deleted": { "type": "boolean" }, + "deviceTokens": { "type": "array", "items": { "type": "string" } }, + "cta": { "$ref": "#/components/schemas/MessageCTA" }, + "status": { "type": "string", "enum": ["sent", "error", "warning"] }, + "payload": { + "type": "object", + "description": "The payload that was used to send the notification trigger" + }, + "overrides": { + "type": "object", + "description": "Provider specific overrides used when triggering the notification" + } + }, + "required": [ + "_templateId", + "_environmentId", + "_messageTemplateId", + "_organizationId", + "_notificationId", + "_subscriberId", + "_feedId", + "_jobId", + "transactionId", + "content", + "channel", + "read", + "seen", + "deleted", + "cta", + "status", + "payload", + "overrides" + ] + }, + "FeedResponseDto": { + "type": "object", + "properties": { + "_id": { "type": "string" }, + "name": { "type": "string" }, + "identifier": { "type": "string" }, + "_environmentId": { "type": "string" }, + "_organizationId": { "type": "string" } + }, + "required": ["name", "identifier", "_environmentId", "_organizationId"] + }, + "UnseenCountResponse": { + "type": "object", + "properties": { "count": { "type": "number" } }, + "required": ["count"] + }, + "MessageMarkAsRequestDto": { + "type": "object", + "properties": { + "messageId": { + "oneOf": [ + { "type": "string" }, + { "type": "array", "items": { "type": "string" } } + ] + }, + "markAs": { + "enum": ["read", "seen", "unread", "unseen"], + "type": "string" + } + }, + "required": ["messageId", "markAs"] + }, + "MessageEntity": { "type": "object", "properties": {} }, + "MarkAllMessageAsRequestDto": { + "type": "object", + "properties": { + "feedIdentifier": { + "oneOf": [ + { "type": "string" }, + { "type": "array", "items": { "type": "string" } } + ], + "description": "Optional feed identifier or array of feed identifiers" + }, + "markAs": { + "enum": ["read", "seen", "unread", "unseen"], + "type": "string", + "description": "Mark all subscriber messages as read, unread, seen or unseen" + } + }, + "required": ["markAs"] + }, + "MessageResponseDto": { + "type": "object", + "properties": { + "_id": { "type": "string" }, + "_templateId": { "type": "string" }, + "_environmentId": { "type": "string" }, + "_messageTemplateId": { "type": "string" }, + "_organizationId": { "type": "string" }, + "_notificationId": { "type": "string" }, + "_subscriberId": { "type": "string" }, + "subscriber": { + "$ref": "#/components/schemas/SubscriberResponseDto" + }, + "template": { "$ref": "#/components/schemas/WorkflowResponse" }, + "templateIdentifier": { "type": "string" }, + "createdAt": { "type": "string" }, + "lastSeenDate": { "type": "string" }, + "lastReadDate": { "type": "string" }, + "content": { + "oneOf": [ + { "$ref": "#/components/schemas/EmailBlock" }, + { "type": "string" } + ] + }, + "transactionId": { "type": "string" }, + "subject": { "type": "string" }, + "channel": { + "enum": ["in_app", "email", "sms", "chat", "push"], + "type": "string" + }, + "read": { "type": "boolean" }, + "seen": { "type": "boolean" }, + "email": { "type": "string" }, + "phone": { "type": "string" }, + "directWebhookUrl": { "type": "string" }, + "providerId": { "type": "string" }, + "deviceTokens": { "type": "array", "items": { "type": "string" } }, + "title": { "type": "string" }, + "cta": { "$ref": "#/components/schemas/MessageCTA" }, + "_feedId": { "type": "string", "nullable": true }, + "status": { "type": "string", "enum": ["sent", "error", "warning"] }, + "errorId": { "type": "string" }, + "errorText": { "type": "string" }, + "payload": { + "type": "object", + "description": "The payload that was used to send the notification trigger" + }, + "overrides": { + "type": "object", + "description": "Provider specific overrides used when triggering the notification" + } + }, + "required": [ + "_templateId", + "_environmentId", + "_messageTemplateId", + "_organizationId", + "_notificationId", + "_subscriberId", + "createdAt", + "content", + "transactionId", + "channel", + "read", + "seen", + "cta", + "status", + "errorId", + "errorText", + "payload", + "overrides" + ] + }, + "MarkMessageActionAsSeenDto": { + "type": "object", + "properties": { + "status": { + "enum": ["pending", "done"], + "type": "string", + "description": "Message action status" + }, + "payload": { + "type": "object", + "description": "Message action payload" + } + }, + "required": ["status"] + }, + "CreateFeedRequestDto": { + "type": "object", + "properties": { "name": { "type": "string" } }, + "required": ["name"] + }, + "CreateLayoutResponseDto": { + "type": "object", + "properties": { "_id": { "type": "string" } }, + "required": ["_id"] + }, + "GetLayoutResponseDto": { + "type": "object", + "properties": { + "_id": { "type": "string" }, + "_organizationId": { "type": "string" }, + "_environmentId": { "type": "string" }, + "_creatorId": { "type": "string" }, + "name": { "type": "string" }, + "identifier": { "type": "string" }, + "description": { "type": "string" }, + "channel": { + "enum": ["in_app", "email", "sms", "chat", "push"], + "type": "string" + }, + "content": { "type": "string" }, + "contentType": { "type": "string" }, + "variables": { "type": "array", "items": { "type": "object" } }, + "isDefault": { "type": "boolean" }, + "isDeleted": { "type": "boolean" }, + "createdAt": { "type": "string" }, + "updatedAt": { "type": "string" }, + "_parentId": { "type": "string" } + }, + "required": [ + "_organizationId", + "_environmentId", + "_creatorId", + "name", + "identifier", + "channel", + "content", + "contentType", + "isDefault", + "isDeleted" + ] + }, + "UpdateLayoutResponseDto": { + "type": "object", + "properties": { + "_id": { "type": "string" }, + "_organizationId": { "type": "string" }, + "_environmentId": { "type": "string" }, + "_creatorId": { "type": "string" }, + "name": { "type": "string" }, + "identifier": { "type": "string" }, + "description": { "type": "string" }, + "channel": { + "enum": ["in_app", "email", "sms", "chat", "push"], + "type": "string" + }, + "content": { "type": "string" }, + "contentType": { "type": "string" }, + "variables": { "type": "array", "items": { "type": "object" } }, + "isDefault": { "type": "boolean" }, + "isDeleted": { "type": "boolean" }, + "createdAt": { "type": "string" }, + "updatedAt": { "type": "string" }, + "_parentId": { "type": "string" } + }, + "required": [ + "_organizationId", + "_environmentId", + "_creatorId", + "name", + "identifier", + "channel", + "content", + "contentType", + "isDefault", + "isDeleted" + ] + }, + "UpdateLayoutRequestDto": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "User defined custom name and provided by the user that will name the Layout updated." + }, + "identifier": { + "type": "string", + "description": "User defined custom key that will be a unique identifier for the Layout updated." + }, + "description": { + "type": "string", + "description": "User defined description of the layout" + }, + "content": { + "type": "string", + "description": "User defined content for the layout." + }, + "variables": { + "description": "User defined variables to render in the layout placeholders.", + "type": "array", + "items": { "type": "object" } + }, + "isDefault": { + "type": "boolean", + "description": "Variable that defines if the layout is chosen as default when creating a layout." + } + }, + "required": ["identifier"] + }, + "DeleteMessageResponseDto": { + "type": "object", + "properties": { + "acknowledged": { + "type": "boolean", + "description": "A boolean stating the success of the action" + }, + "status": { + "type": "string", + "description": "The status enum for the performed action", + "enum": ["deleted"] + } + }, + "required": ["acknowledged", "status"] + }, + "CreateTopicResponseDto": { "type": "object", "properties": {} }, + "CreateTopicRequestDto": { + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "User defined custom key and provided by the user that will be an unique identifier for the Topic created." + }, + "name": { + "type": "string", + "description": "User defined custom name and provided by the user that will name the Topic created." + } + }, + "required": ["key", "name"] + }, + "AddSubscribersRequestDto": { + "type": "object", + "properties": { + "subscribers": { + "description": "List of subscriber identifiers that will be associated to the topic", + "type": "array", + "items": { "type": "string" } + } + }, + "required": ["subscribers"] + }, + "TopicSubscriberDto": { + "type": "object", + "properties": { + "_organizationId": { "type": "string" }, + "_environmentId": { "type": "string" }, + "_subscriberId": { "type": "string" }, + "_topicId": { "type": "string" }, + "topicKey": { "type": "string" }, + "externalSubscriberId": { "type": "string" } + }, + "required": [ + "_organizationId", + "_environmentId", + "_subscriberId", + "_topicId", + "topicKey", + "externalSubscriberId" + ] + }, + "RemoveSubscribersRequestDto": { + "type": "object", + "properties": { + "subscribers": { + "description": "List of subscriber identifiers that will be removed to the topic", + "type": "array", + "items": { "type": "string" } + } + }, + "required": ["subscribers"] + }, + "TopicDto": { + "type": "object", + "properties": { + "_id": { "type": "string" }, + "_organizationId": { "type": "string" }, + "_environmentId": { "type": "string" }, + "key": { "type": "string" }, + "name": { "type": "string" }, + "subscribers": { "type": "array", "items": { "type": "string" } } + }, + "required": [ + "_organizationId", + "_environmentId", + "key", + "name", + "subscribers" + ] + }, + "FilterTopicsResponseDto": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { "$ref": "#/components/schemas/TopicDto" } + }, + "page": { "type": "number" }, + "pageSize": { "type": "number" }, + "totalCount": { "type": "number" } + }, + "required": ["data", "page", "pageSize", "totalCount"] + }, + "GetTopicResponseDto": { + "type": "object", + "properties": { + "_id": { "type": "string" }, + "_organizationId": { "type": "string" }, + "_environmentId": { "type": "string" }, + "key": { "type": "string" }, + "name": { "type": "string" }, + "subscribers": { "type": "array", "items": { "type": "string" } } + }, + "required": [ + "_organizationId", + "_environmentId", + "key", + "name", + "subscribers" + ] + }, + "RenameTopicResponseDto": { + "type": "object", + "properties": { + "_id": { "type": "string" }, + "_organizationId": { "type": "string" }, + "_environmentId": { "type": "string" }, + "key": { "type": "string" }, + "name": { "type": "string" }, + "subscribers": { "type": "array", "items": { "type": "string" } } + }, + "required": [ + "_organizationId", + "_environmentId", + "key", + "name", + "subscribers" + ] + }, + "RenameTopicRequestDto": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "User defined custom name and provided by the user to rename the topic." + } + }, + "required": ["name"] + }, + "GetTenantResponseDto": { + "type": "object", + "properties": { + "_id": { "type": "string" }, + "identifier": { "type": "string" }, + "name": { "type": "string" }, + "data": { "type": "object" }, + "_environmentId": { "type": "string" }, + "createdAt": { "type": "string" }, + "updatedAt": { "type": "string" } + }, + "required": [ + "_id", + "identifier", + "_environmentId", + "createdAt", + "updatedAt" + ] + }, + "CreateTenantResponseDto": { + "type": "object", + "properties": { + "_id": { "type": "string" }, + "identifier": { "type": "string" }, + "name": { "type": "string" }, + "data": { "type": "object" }, + "_environmentId": { "type": "string" }, + "createdAt": { "type": "string" }, + "updatedAt": { "type": "string" } + }, + "required": [ + "_id", + "identifier", + "_environmentId", + "createdAt", + "updatedAt" + ] + }, + "CreateTenantRequestDto": { + "type": "object", + "properties": { + "identifier": { "type": "string" }, + "name": { "type": "string" }, + "data": { "type": "object" } + }, + "required": ["identifier", "name"] + }, + "UpdateTenantResponseDto": { + "type": "object", + "properties": { + "_id": { "type": "string" }, + "identifier": { "type": "string" }, + "name": { "type": "string" }, + "data": { "type": "object" }, + "_environmentId": { "type": "string" }, + "createdAt": { "type": "string" }, + "updatedAt": { "type": "string" } + }, + "required": [ + "_id", + "identifier", + "_environmentId", + "createdAt", + "updatedAt" + ] + }, + "UpdateTenantRequestDto": { + "type": "object", + "properties": { + "identifier": { "type": "string" }, + "name": { "type": "string" }, + "data": { "type": "object" } + } + } + }, + "headers": { + "Content-Type": { + "required": true, + "description": "The MIME type of the response body.", + "schema": { "type": "string" }, + "example": "application/json" + }, + "RateLimit-Limit": { + "required": false, + "description": "The number of requests that the client is permitted to make per second. The actual maximum may differ when burst is enabled.", + "schema": { "type": "string" }, + "example": "100" + }, + "RateLimit-Remaining": { + "required": false, + "description": "The number of requests remaining until the next window.", + "schema": { "type": "string" }, + "example": "93" + }, + "RateLimit-Reset": { + "required": false, + "description": "The remaining seconds until a request of the same cost will be refreshed.", + "schema": { "type": "string" }, + "example": "8" + }, + "RateLimit-Policy": { + "required": false, + "description": "The rate limit policy that was used to evaluate the request.", + "schema": { "type": "string" }, + "example": "100;w=1;burst=110;comment=\"token bucket\";category=\"trigger\";cost=\"single\"" + }, + "Retry-After": { + "required": false, + "description": "The number of seconds after which the client may retry the request that was previously rejected.", + "schema": { "type": "string" }, + "example": "8" + }, + "Idempotency-Key": { + "required": false, + "description": "The idempotency key used to evaluate the request.", + "schema": { "type": "string" }, + "example": "8" + }, + "Idempotency-Replay": { + "required": false, + "description": "Whether the request was a replay of a previous request.", + "schema": { "type": "string" }, + "example": "true" + }, + "Link": { + "required": false, + "description": "A link to the documentation.", + "schema": { "type": "string" }, + "example": "https://docs.novu.co/" + } + } + }, + "externalDocs": { + "description": "Novu Documentation", + "url": "https://docs.novu.co" + }, + "x-speakeasy-name-override": [ + { "operationId": "^.*get.*", "methodNameOverride": "retrieve" }, + { "operationId": "^.*retrieve.*", "methodNameOverride": "retrieve" }, + { "operationId": "^.*create.*", "methodNameOverride": "create" }, + { "operationId": "^.*update.*", "methodNameOverride": "update" }, + { "operationId": "^.*list.*", "methodNameOverride": "list" }, + { "operationId": "^.*delete.*", "methodNameOverride": "delete" }, + { "operationId": "^.*remove.*", "methodNameOverride": "delete" } + ], + "x-speakeasy-retries": { + "strategy": "backoff", + "backoff": { + "initialInterval": 500, + "maxInterval": 30000, + "maxElapsedTime": 3600000, + "exponent": 1.5 + }, + "statusCodes": ["408", "409", "429", "5XX"], + "retryConnectionErrors": true + } +} diff --git a/platform/inbound-parse-webhook.mdx b/platform/inbound-parse-webhook.mdx deleted file mode 100644 index 0745685f..00000000 --- a/platform/inbound-parse-webhook.mdx +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: "Inbound Parse Webhook" -description: "Learn how to process incoming emails for a domain or subdomain" -icon: "inbox-full" ---- - -import CloudOnlyFeature from "/snippets/cloud-only-feature.mdx" - -Inbound Webhook is a feature that allows processing of incoming emails for a domain or subdomain. -The feature parses contents of an email and POSTs the information to a specified URL in a `multipart/form-data` format. - - - -## Steps to set up Inbound Webhook - -1. Set up an MX Record: - - Log in to your domain host's website and navigate to the MX Records page. - - Create a new MX record for the subdomain you want to process incoming email (e.g. reply.yourdomain.com). - - You must create a new record for this as this feature does not work if the MX record is already set on the domain. - Here is an example with [route 53](https://medium.com/@deep_blue_day/how-to-create-a-subdomain-in-amazon-route-53-81918654f5bf) - - - Assign the MX record a priority of 10 and point it to the specified inbound mail server that located on the admin dashboard on the Email Settings. - - For US, use `inbound-mail.novu.co` and For EU, use - `eu.inbound-mail.novu.co` - -2. Add Domain to allowed list: - - Log in to the dashboard. - - Go to the Setting tab in the left vertical navbar. - - Navigate to Email Settings on the settings page. - - Add your domain to the allowed domain box. - - You can only add one Inbound Webhook domain per organization. - -3. Enable Inbound Parse and Set Webhook URL: - - Log in to the dashboard. - - Navigate to the Workflow Editor. - - Select the email step. - - Enable the Inbound Parse feature. - - Set the Webhook URL to the location where you want the parsed data to be POSTed. - -The Webhook URL must be publicly accessible without authentication. diff --git a/platform/webhooks-for-providers.mdx b/platform/webhooks-for-providers.mdx deleted file mode 100644 index b2819e7f..00000000 --- a/platform/webhooks-for-providers.mdx +++ /dev/null @@ -1,41 +0,0 @@ ---- -title: "Webhooks for Providers" -description: "Learn how to setup webhook support for a provider." -icon: "webhook" ---- - -import CloudOnlyFeature from "/snippets/cloud-only-feature.mdx" - -Novu provides a URL that you can use to setup webhook support for your integrated provider. - -This enables to receive much more detailed information from the provider about the outgoing messages in the [Activity Feed](/activity-feed/introduction) section. - -Currently, only `Email` and `SMS` channels are supported with limited providers. Support for more providers will be added in future. - - - -## Get your Webhook URL - -To get your provider specific webhook URL, follow the below steps: - -- Open your already connected provider from the `Integration Store` page. -- Scroll down the modal to find the `Webhook URL` field. -- Copy the given URL to configure it on the provider's webhook setup page. - - - - - -
    - - Webhook URL will only be visible for connected providers and after you've connected the provider. - -## Supported Providers - -- [Mailjet](/channels-and-providers/email/mailjet) -- [Mandrill](/channels-and-providers/email/mandrill) -- [Postmark](/channels-and-providers/email/postmark) -- [Sendgrid](/channels-and-providers/email/sendgrid) -- [Sendinblue](/channels-and-providers/email/sendinblue) -- [Telnyx](/channels-and-providers/sms/telnyx) -- [Twilio](/channels-and-providers/sms/twilio) diff --git a/quickstart/express.mdx b/quickstart/express.mdx new file mode 100644 index 00000000..e21164e3 --- /dev/null +++ b/quickstart/express.mdx @@ -0,0 +1,53 @@ +--- +title: "How to send notifications with Express.js and Novu Framework" +sidebarTitle: "Express.js" +--- + +import LocalStudio from "/snippets/quickstart/start-studio.mdx"; +import DeployApp from "/snippets/quickstart/deploy.mdx"; +import TestStep from "/snippets/quickstart/test.mdx"; +import PackagesStep from "/snippets/quickstart/packages.mdx"; +import SecretStep from "/snippets/quickstart/secret.mdx"; +import NextStepsStep from "/snippets/quickstart/next-steps.mdx"; +import WorkflowStep from "/snippets/quickstart/workflow.mdx"; + +In this guide, we will add a Novu [Bridge Endpoint](/concepts/endpoint) to a Express.js application and send our first test workflow. + + + + + + + + ```typescript app/server/api/novu.ts + import { serve } from "@novu/framework/express"; + import { testWorkflow } from "../novu/workflows"; + + app.use(express.json()); // Required for Novu POST requests + app.use( "/api/novu", serve({ workflows: [testWorkflow] }) ); + ``` + + + + + + Add a `novu` folder in your app folder as such ```novu/workflows.ts``` that will contain your workflow definitions. + + + + + + Start your Express server with the Novu Endpoint configured. + + If your Express application is running on other than `4000` port, restart the `npx novu dev` command with the port: + + ```tsx + npx novu@latest dev --port + ``` + + + + + + + diff --git a/quickstart/h3.mdx b/quickstart/h3.mdx new file mode 100644 index 00000000..63105c21 --- /dev/null +++ b/quickstart/h3.mdx @@ -0,0 +1,58 @@ +--- +title: "How to send notifications with H3 and Novu Framework" +sidebarTitle: "H3" +--- + +import LocalStudio from "/snippets/quickstart/start-studio.mdx"; +import DeployApp from "/snippets/quickstart/deploy.mdx"; +import TestStep from "/snippets/quickstart/test.mdx"; +import PackagesStep from "/snippets/quickstart/packages.mdx"; +import SecretStep from "/snippets/quickstart/secret.mdx"; +import NextStepsStep from "/snippets/quickstart/next-steps.mdx"; +import WorkflowStep from "/snippets/quickstart/workflow.mdx"; + +In this guide, we will add a Novu [Bridge Endpoint](/concepts/endpoint) to a H3 application and send our first test workflow. + + + + + + + ```typescript app/server/api/novu.ts + import { createApp, eventHandler, toNodeListener } from "h3"; + import { serve } from "@novu/framework/h3"; + import { createServer } from "node:http"; + import { testWorkflow } from "./novu/workflows"; + + const app = createApp(); + + app.use("/api/novu", eventHandler(serve({ workflows: [testWorkflow] }) )); + + createServer(toNodeListener(app)).listen(4000); + ``` + + + + + + Add a `novu` folder in your app folder as such ```novu/workflows.ts``` that will contain your workflow definitions. + + + + + + + Start your H3 server with the Novu Endpoint configured. + + If your Remix application is running on other than `4000` port, restart the `npx novu dev` command with the port: + + ```tsx + npx novu@latest dev --port + ``` + + + + + + + diff --git a/quickstart/nextjs.mdx b/quickstart/nextjs.mdx new file mode 100644 index 00000000..c0d744f4 --- /dev/null +++ b/quickstart/nextjs.mdx @@ -0,0 +1,153 @@ +--- +title: "How to send notifications with Next.js and Novu Framework" +sidebarTitle: "Next.js" +--- + +import LocalStudio from "/snippets/quickstart/start-studio.mdx"; +import DeployApp from "/snippets/quickstart/deploy.mdx"; +import TestStep from "/snippets/quickstart/test.mdx"; +import NextStepsStep from "/snippets/quickstart/next-steps.mdx"; + +In this guide, we will add a Novu [Bridge Endpoint](/concepts/endpoint) to a Next.js application and send our first test workflow. + + + + + This link can be copied right from the onboarding guide on the Novu Studio or can always be copied from the [API Keys](https://dashboard.novu.co/api-keys/Development) page on the Novu Dashboard. + + ```bash + npx create-novu-app --secret-key= + ``` + + The sample application will create an `.env` file containing the `NOVU_SECRET_KEY` environment variable required + for securing your endpoint. And a sample workflow demonstrating the flexibility of Novu using Step Controls. + + + **Install required packages** + + ```bash + npm install @novu/framework @react-email/components react-email zod zod-to-json-schema + ``` + + This will install + + - **`@novu/framework`** SDK Package + - **React Email** (Recommended) - For writing your email templates with React + - **Zod** (Recommended) - For end-to-end type safety for your Payload and Step Controls + + **Add a Novu API Endpoint** + + + + ```typescript App Router (app/api/novu/route.ts) + import { serve } from "@novu/framework/next"; + import { myWorkflow } from "../../novu/workflows"; + + export const { GET, POST, PUT, OPTIONS } = serve({ workflows: [myWorkflow] }); + ``` + + ```typescript Pages Router (pages/api/novu.ts) + import { serve } from '@novu/framework/next'; + import { testWorkflow } from '../../novu/workflows'; + + export default serve({ workflows: [testWorkflow] }); + ``` + + + **Add a Novu Secret Key Environment Variable** + + Add `NOVU_SECRET_KEY` environment variable to your `.env` + + ```env + NOVU_SECRET_KEY= + ``` + + **Create your workflow definition** + + Add a `novu` folder that will contain your workflow definitions + + ```tsx app/novu/workflows.ts + import { workflow } from '@novu/framework'; + import { renderEmail } from './emails/test-email'; + import { z } from 'zod'; + + export const testWorkflow = workflow('test-workflow', async ({ step, payload }) => { + await step.email('send-email', async (controls) => { + return { + subject: controls.subject, + body: renderEmail(payload.userName), + }; + }, + { + controlSchema: z.object({ + subject: z.string().default('A Successful Test on Novu from {{userName}}'), + }), + }); + }, { + payloadSchema: z.object({ + userName: z.string().default('John Doe'), + }), + }); + ``` + + **Create your React Email Template (Optional)** + + Add a new email template + + ```typescript app/novu/emails/test-email.tsx + import { + Body, + Container, + Head, + Html, + render, + } from '@react-email/components'; + import * as React from "react"; + + interface TestEmailProps { + name: string + } + + export const TestEmailTemplate = ({ name }: TestEmailProps) => { + return ( + + + + + Hello {name} welcome to your first React E-mail template! + + + + ); + }; + + export default TestEmailTemplate; + + export function renderEmail(name: string) { + return render(); + } + ``` + + + + To start your boilerplate Next.js server with the Novu Endpoint configured, run the following command: + + ```tsx + cd my-novu-app && npm run dev + ``` + + The sample application will start on [`https://localhost:4000`](https://localhost:4000) and your novu endpoint will be exposed at `/api/novu` served by the Next.js API. + + If your Next.js application is running on other than `4000` port, restart the `novu dev` command with the port: + + ```tsx + npx novu@latest dev --port + ``` + + + + + + + + diff --git a/quickstart/nuxt.mdx b/quickstart/nuxt.mdx new file mode 100644 index 00000000..3eced114 --- /dev/null +++ b/quickstart/nuxt.mdx @@ -0,0 +1,55 @@ +--- +title: "How to send notifications with Nuxt and Novu Framework" +sidebarTitle: "Nuxt" +--- + +import LocalStudio from "/snippets/quickstart/start-studio.mdx"; +import DeployApp from "/snippets/quickstart/deploy.mdx"; +import TestStep from "/snippets/quickstart/test.mdx"; +import PackagesStep from "/snippets/quickstart/packages.mdx"; +import SecretStep from "/snippets/quickstart/secret.mdx"; +import NextStepsStep from "/snippets/quickstart/next-steps.mdx"; +import WorkflowStep from "/snippets/quickstart/workflow.mdx"; + +In this guide, we will add a Novu [Bridge Endpoint](/concepts/endpoint) to a Nuxt application and send our first test workflow. + + + + + + + ```typescript app/server/api/novu.ts + import { serve } from '@novu/framework/nuxt'; + import { testWorkflow } from "../novu/workflows"; + + export default defineEventHandler(serve({ workflows: [myWorkflow] })); + ``` + + + + + + Add a `novu` folder in your app folder as such ```app/server/api/novu.ts``` that will contain your workflow definitions. + + + + + + To start your Remix server with the Novu Endpoint configured, run the following command: + + ```bash + cd my-novu-app && npm run dev + ``` + + If your Remix application is running on other than `4000` port, restart the `npx novu dev` command with the port: + + ```bash + npx novu@latest dev --port + ``` + + + + + + + diff --git a/quickstart/overview.mdx b/quickstart/overview.mdx new file mode 100644 index 00000000..aade3d39 --- /dev/null +++ b/quickstart/overview.mdx @@ -0,0 +1,27 @@ +--- +title: "Quickstart" +sidebarTitle: "Overview" +description: "Start building with Novu by following our Quick Start guides. These guides provide step-by-step instructions for integrating Novu into your application." +--- + + + + + + + + + + + + + + + + + + + diff --git a/quickstart/remix.mdx b/quickstart/remix.mdx new file mode 100644 index 00000000..8c54ca09 --- /dev/null +++ b/quickstart/remix.mdx @@ -0,0 +1,61 @@ +--- +title: "How to send notifications with Remix and Novu Framework" +sidebarTitle: "Remix" +--- + +import LocalStudio from "/snippets/quickstart/start-studio.mdx"; +import DeployApp from "/snippets/quickstart/deploy.mdx"; +import TestStep from "/snippets/quickstart/test.mdx"; +import PackagesStep from "/snippets/quickstart/packages.mdx"; +import SecretStep from "/snippets/quickstart/secret.mdx"; +import WorkflowCode from "/snippets/quickstart/workflow.mdx"; + +In this guide, we will add a Novu [Bridge Endpoint](/concepts/endpoint) to a Remix application and send our first test workflow. + + + + + + + This guide is based on Remix Offical [Quick Start](https://remix.run/docs/en/main/start/quickstart). + + + ```typescript app/routes/api.novu.ts + import { serve } from "@novu/framework/remix"; + import { testWorkflow } from "../novu/workflows"; + + const handler = serve({ + workflows: [testWorkflow] + }); + + export { handler as action, handler as loader }; + ``` + + + + + + Add a `novu` folder in your app folder as such ```app/novu/workflows.ts``` that will contain your workflow definitions. + + + + + + To start your Remix server with the Novu Endpoint configured, run the following command: + + ```bash + cd my-novu-app && npm run dev + ``` + + Remix application default port is 5173. For that to work, restart Novu Studio and point it to the right port: + + ```bash + npx novu@latest dev --port + ``` + + + + + + + diff --git a/quickstart/svelte.mdx b/quickstart/svelte.mdx new file mode 100644 index 00000000..f839c07e --- /dev/null +++ b/quickstart/svelte.mdx @@ -0,0 +1,55 @@ +--- +title: "How to send notifications with Svelte and Novu Framework" +sidebarTitle: "Svelte" +--- + +import LocalStudio from "/snippets/quickstart/start-studio.mdx"; +import DeployApp from "/snippets/quickstart/deploy.mdx"; +import TestStep from "/snippets/quickstart/test.mdx"; +import PackagesStep from "/snippets/quickstart/packages.mdx"; +import SecretStep from "/snippets/quickstart/secret.mdx"; +import NextStepsStep from "/snippets/quickstart/next-steps.mdx"; +import WorkflowCode from "/snippets/quickstart/workflow.mdx"; + +In this guide, we will add a Novu [Bridge Endpoint](/concepts/endpoint) to a Svelte application and send our first test workflow. + + + + + + + + ```typescript src/routes/api/novu/+server.ts + import { testWorkflow } from '$lib/novu/workflows'; + import { serve } from '@novu/framework/sveltekit'; + + export const { GET, POST, OPTIONS } = serve({ workflows: [testWorkflow] }); + ``` + + + + + Add a `novu` folder in your lib folder as such ```src/lib/novu/workflows.ts``` that will contain your workflow definitions. + + + + + + To start your Svelte server with the Novu Endpoint configured, run the following command: + + ```tsx + cd my-novu-app && npm run dev + ``` + + Svelte application default port is 5173. For that to work, restart Novu Studio and point it to the right port: + + ```tsx + npx novu@latest dev --port + ``` + + + + + + + diff --git a/quickstarts/.NET.mdx b/quickstarts/.NET.mdx deleted file mode 100644 index 5ec34eb6..00000000 --- a/quickstarts/.NET.mdx +++ /dev/null @@ -1,336 +0,0 @@ ---- -title: '.NET' -description: 'Learn how to use Novu to quickly send multi-channel (SMS, Email, Chat, Push) notifications with .NET.' -icon: 'microsoft' ---- - -import SupportedChannelsSnippet from "/snippets/supported-channels.mdx"; - -Learn how to use Novu to quickly send multi-channel (SMS, Email, Chat, Push) notifications from a .NET app. - -## Requirements - -To follow the steps in this quickstart, you'll need: - -- A Novu account. [Sign up for free](http://web.novu.co/?utm_campaign=docs-gs-quick-dotnet) if you don’t have one yet. -- A working .NET development environment with a .NET version of .NET Core 2.0+ and .NET Framework of 4.6.1+. - -You can also [view the completed code](https://github.com/novuhq/novu-dotnet-quickstart) of this quick start in a GitHub repo. - -## Install Novu .NET SDK - -The [.NET SDK](https://github.com/novuhq/novu-dotnet) provides a fluent and expressive interface for interacting with Novu's API and managing notifications. - -Now install the Novu .NET SDK by running the following command in your terminal: - -```bash -dotnet add package Novu - -``` - -Otherwise, modify your .csproj file add the following to it: - -```xml - - - - -``` - -Next, install the SDK by running the following `dotnet` command: - -```bash -dotnet restore - -``` - -## Initialize & Configure the Novu SDK - -Create a new file, `Program.cs` in your application and add the following code to it: - -Program.cs: - -```csharp -using Novu.DTO; -using Novu.DTO.Topics; -using Novu.Models; -using Novu; - - var novuConfiguration = new NovuClientConfiguration -{ - ApiKey = "", -}; - -var novu = new NovuClient(novuConfiguration); -``` - -Replace the `ApiKey`’s value with the authentic key from the **API Key** section of your [Novu Dashboard](https://web.novu.co/settings?utm_campaign=docs-quick-dotnet) and be sure to use your specific Novu instance URL. - - -Please do not hardcode your credentials in a file in production. Use environment variables instead. - -## Set Up A Channel Provider - -A channel provider is a service that provides one or more notification functionality such as sending an email, SMS, push notification etc. Our [integration store](https://web.novu.co/integrations?utm_campaign=docs-quick-dotnet) includes four channels: Email, SMS, Chat, and Push. These channels have multiple providers associated with them. - -| Channel | Providers | -| --- | --- | -| Email | MailGun, Mandrill, MailJet, Amazon SES, Sendgrid, Postmark, Netcore | -| SMS | Twilio, Amazon SNS, Plivo, SMS, SMSCentral, Kannel, Infobip, Termii | -| Chat | Mattermost, Slack, Microsoft Teams, Discord | -| Push | FCM, APNS, Expo | - -Only one provider can be **active** per **channel**. Connect any of your favourite providers to get started. The email channel comes with Novu's email provider, which is active by default and includes 300 credits. - -## Create A Workflow - -A workflow is a blueprint for the notifications that will be sent. It holds the entire flow of messages sent to the subscriber. This is where all the different channels are tied together under a single entity. - -The workflow includes the following: - -- Workflow name and Identifier -- Channel-tailored content: - - - - -Proper authorization needs to be set for the Chat channel for subscribers. - -Please proceed to create a workflow. - -1. Click **Workflows** on the left sidebar of your Novu dashboard. -2. Click the **Create Workflow** button on the top right. -3. The name of the new workflow is currently **Untitled.** Rename it to a more suitable title. -4. Select **Email** as the channel you want to add. - -5. Click on the recently added channel, fill the email subject and click “Update”. - -6. Click on the “Test” tab and send a test email to verify your workflow. - -You should get an email within seconds. Yaaay, you have successfully sent your first notification via the Novu dashboard! Now, let’s take it a step further to trigger notifications via code. - -## Create A Subscriber - -The recipients of a triggered notification are called subscribers. - -Click “Subscribers” on the left sidebar of the Novu dashboard to see all subscribers. By default, the dashboard will display a subscriber, as you were added automatically during sign-up. - -Now, let's create a subscriber on Novu. Copy and paste the following code to do so: - -```csharp -// Create subscriber - -var newSubscriberDto = new SubscriberCreateData -{ - SubscriberId = "77809", //replace with system_internal_user_id - FirstName = "John", - LastName = "Doe", - Email = "benlin1994@gmail.com", - Data = new List{ - new AdditionalDataDto - { - Key = "External ID", - Value = "1122334455" - }, - new AdditionalDataDto - { - Key = "Job Title", - Value = "Software Engineer" - } - } -}; - -var subscriber = await novu.Subscriber.Create(newSubscriberDto); -``` - -Run the code in your terminal like so: - -```bash -dotnet run Program.cs - -``` - -You should see the subscriber on your Novu dashboard. - - - -I’d like to publicly announce that `abc@gmail.com` is a random unlikely email your users will have. To update this to an alternative email, you can call the `UpdateSubscriber` method like so: - -Program.cs: - -```csharp -// Update subscriber detail -var subscriber7789 = await novu.Subscriber.GetSubscriber("7789"); - -subscriber7789.Email = "validemail@gmail.com"; // replace with valid email -subscriber7789.FirstName = ""; // optional -subscriber7789.LastName = ""; // optional - -var updatedSubscriber = await novu.Subscriber.Update("7789",subscriber7789); -``` - -Other valid fields that can be updated are `phone`, `avatar`, and `data` . The `data` field can accept an array of metadata that you want to attach to the subscriber. - - - -To make all of your app users subscribers, you need to programmatically add them to Novu. - - -## Trigger A Notification - -Copy and paste the following code into your app to trigger a notification: - -OnboardEventPayload.cs: - -```csharp -// OnboardEventPayload.cs -public class OnboardEventPayload -{ - [JsonProperty("username")] - public string Username { get; set; } - - [JsonProperty("welcomeMessage")] - public string WelcomeMessage { get; set; } -} - -``` - -Program.cs: - -```csharp -var onboardingMessage = new OnboardEventPayload -{ - Username = "jdoe", - WelcomeMessage = "Welcome to novu-dotnet" -}; - -var payload = new EventCreateData() -{ - EventName = "onboarding", //make sure you have a trigger named "onboarding" - To = { SubscriberId = "7789" }, - Payload = onboardingMessage -}; - -var trigger = await novu.Event.Trigger(payload); - -if (trigger.Data.Acknowledged) -{ - Console.WriteLine("Trigger has been created."); -} - -``` - -Before running the code, make sure you understand the following: - -- The value of `EventName` should be the workflow’s trigger ID/slug(it's the value all in small caps). - -- The value of `Payload` is an array of the data that you want to be dynamically injected into the workflow content. -- The value of `SubscriberId` is the id of the subscriber on Novu. Replace `7789` with your subscriberId. - -Run the code to trigger a notification! - -```bash -dotnet run Program.cs - -``` - -Next, we’ll learn how to send notifications to different groups of subscribers easily via **Topics.** - -## Topics - -Novu provides a simple API that offers an easy interface for triggering notifications to multiple subscribers at once. This API is called **Topics** and allows users to manage their bulk notifications without having to implement complex loops. - -A topic is identified by a custom key and this key will be the identifier used when triggering notifications. You can assign a name to a topic for descriptive purposes. This name does not need to be unique and can be changed programmatically. - - - -The topic key should be unique and can't be changed once chosen. Novu also safeguards for key uniqueness behind the scenes. - - -A topic can have multiple subscribers who will receive a notification whenever a message is sent to the topic. - -## Create a Topic - -Copy and paste the following code into your app to create a topic: - -```csharp -// Create Topic -var topicRequest = new TopicCreateData -{ - Key = "frontend-users", - Name = "All frontend users", - -}; - -var topic = await novu.Topic.Create(topicRequest); -``` - -Before running the code, make sure you understand the following: - -- When creating a `Key`, ensure it is unique and accurately identifies the topic. Document naming conventions and communicate them to team members to avoid confusion and ensure a smooth workflow. -- The value of `Name` should be a descriptive topic name. - -## Add Subscribers to a Topic - -Copy and paste the following code into your app to add subscribers to a topic: - -```csharp - -// Add Subscriber to Topic -var topicKey = "frontend-users"; -var subscriberList = new TopicSubscriberUpdateDto -{ - Keys = new List - { - "7789", - "7790", - } -}; - -//Or - -var subscriberList = new TopicSubscriberCreateData(new List -{ - "7789", - "7790" -}); - -var result = await novu.Topic.AddSubscriber(topicKey, subscriberList); -``` - -## Sending a Notification to a Topic - -Thanks to the topics feature, it is possible to trigger a notification to all subscribers assigned to a topic. This helps avoid listing all subscriber identifiers in the `to` field of the notification trigger. - -To trigger a notification to all subscribers of a topic, copy and paste the code below: - -Program.cs - -```csharp -// Send notifications to a topic (all frontend users) - -var onboardingMessage = new OnboardEventPayload -{ - WelcomeMessage = "Welcome to novu-dotnet" -}; - -var payloadTopic = new EventTopicTriggerDto -{ - EventName = "onboarding", - Payload = onboardingMessage, - Topic = { Type = "7789", TopicKey = "frontend-users" }, - -}; - -var triggerTopic = await novu.Event.TriggerTopicAsync(payloadTopic); - -if (triggerTopic.TriggerResponsePayloadDto.Acknowledged); -{ - Console.WriteLine("Trigger has been created."); -} - -``` - -## Conclusion - -Great job! If you've reached this point, you should now have successfully created a subscriber, workflow, configured a channel provider and triggered a notification in your application. diff --git a/quickstarts/angular.mdx b/quickstarts/angular.mdx deleted file mode 100644 index 45b0d689..00000000 --- a/quickstarts/angular.mdx +++ /dev/null @@ -1,348 +0,0 @@ ---- -title: 'Angular' -description: 'Learn how to use Novu to quickly send multi-channel (SMS, Email, Chat, Push) notifications with Angular.' -icon: 'angular' ---- - -import SupportedChannelsSnippet from "/snippets/supported-channels.mdx"; - -Learn how to use Novu to quickly send multi-channel (SMS, Email, Chat, Push) notifications and integrate a rich, customizable and ready-to-use real-time UI In-App notification center in Angular apps. - -## Requirements - -To follow the steps in this quickstart, you'll need: - -- A Novu account. [Sign up for free](http://web.novu.co/?utm_campaign=docs-gs-quick-angluar) if you don’t have one yet -- Angular CLI (Command Line Interface) installed on your machine -- Angular version 15 or higher - -You can also [view the completed code](https://github.com/novuhq/angular-quickstart) of this quick start in a GitHub repo. - -## Create a new Angular app - -To create a new Angular app, open a terminal or command prompt and run the following command: - -```bash -ng new my-app -``` - -Here, `my-app` is the name of your new Angular app. This command will create a new Angular app with a basic file structure and all the necessary dependencies installed. - -Navigate to the app directory by running the following command: - -```bash -cd my-app -``` - -Once you are in the app directory, you can start the development server by running the following command: - -```bash -ng serve -``` - -This command will start the development server and launch your app in the default browser. You can access your app by navigating to `http://localhost:4200/`. - - - -## Install Novu Angular Notification Center Package - -The Novu Angular package provides a Angular component wrapper over the web component that you can use to integrate the notification center into your Angular application. - -Navigate to the root directory of your Angular application. Now install the Angular Notification Center package by running the following command in your terminal: - - -```bash pnpm -pnpm i @novu/notification-center-angular -``` -```bash npm -npm i @novu/notification-center-angular -``` -```bash yarn -yarn add @novu/notification-center-angular -``` - - - -## Configuring Application Environments - -Using the Angular CLI, start by running the [generate environments command](https://angular.io/cli/generate#environments-command) -shown here to create the `src/environments/`directory and configure the project to use these files. - -```bash -ng generate environments -``` - -Navigate to `my-app/src/environments/environment.ts` and add the following variables: `subscriberId`, `applicationIdentifier` - -* **`applicationIdentifier`** can be found here: https://web.novu.co/settings -* **`subscriberId`** can be found here: https://web.novu.co/subscribers - -```tsx -export const environment = { - production: false, - subscriberId: '', - applicationIdentifier: '', -}; -``` - -Copy and paste the same code into `my-app/src/environments/environment.development.ts` - -These variables are needed for the `GET` request our notification center will make to Novu’s API to push notifications into the feed. - -## Adding Novu Module - -Now, navigate to the `app.module.ts` file `(my-app/src/app/app.module.ts)` - -- Import `CUSTOM_ELEMENTS_SCHEMA` from `'@angular/core'` -- Add Novu’s notification center module - -```jsx -import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; -import { AppComponent } from './app.component'; -import { NotificationCenterModule } from '@novu/notification-center-angular'; - -@NgModule({ - declarations: [AppComponent], - imports: [BrowserModule, NotificationCenterModule], - schemas: [CUSTOM_ELEMENTS_SCHEMA], - providers: [], - bootstrap: [AppComponent], -}) -export class AppModule {} -``` - -Head to `my-app/src/app/app.component.ts` file. - -⚠️ The `app.component.ts` file is a critical part of an Angular application, as it defines the root component and provides the foundation for the rest of the app's functionality. - -Now, we are going to import the `environment` variables to make them accessible in the `app.component.html` and the `styles` properties of our notification center - -(there are many properties, but you can discover them later on) - -```tsx -import { Component } from '@angular/core'; -import { environment } from '../environments/environment'; - -@Component({ - selector: 'app-root', - templateUrl: './app.component.html', - styleUrls: ['./app.component.css'], -}) -export class AppComponent { - title = 'my-app'; - - subscriberId = environment.subscriberId; - applicationIdentifier = environment.applicationIdentifier; - - styles = { - bellButton: { - root: { - svg: { - color: 'white', - width: '45px', - height: '40px', - fill: 'white', - }, - }, - dot: { - rect: { - fill: 'rgb(221, 0, 49)', - strokeWidth: '0.2', - stroke: 'white', - width: '3.5px', - height: '3.5px', - }, - left: '40%', - }, - }, - header: { - root: { - backgroundColor: '', - '&:hover': { backgroundColor: '' }, - '.some_class': { color: '' }, - }, - }, - layout: { - root: { - backgroundColor: '', - }, - }, - popover: { - arrow: { - backgroundColor: '', - border: '', - }, - }, - }; - - sessionLoaded = (data: unknown) => { - console.log('loaded', { data }); - }; -} -``` - -The Angular component is generated as a wrapper around the original React component. This approach is clever, as it allows Novu's engineers to focus on creating and developing things in the React way. - -Additionally, many other frameworks can still use the created components using the wrapping approach. - -Now head to the `my-app/tsconfig.json` file, we’re going to add `"allowSyntheticDefaultImports": true` to the `compilerOptions` array. - -```diff -{ - "compilerOptions": { -+ "allowSyntheticDefaultImports": true, - } -} -``` - -## Use & Display The Notification Center Component - -Open the `my-app/src/app/app.component.html` file. - -This file contain the CSS code along with the HTML one - ideally you should separate the CSS to the `my-app/src/app/app.component.css` file, but it’s not mandatory. - -We will add our notification center to the .toolbar div. - -Paste the following into your `app.component.html` file: - -```jsx -
    - -
    -``` - -And in the ` - - - - -
    -

    iFrame Notification Center Icon Bell

    -
    - - -
    -

    Click on the notification bell to see the feed

    -
    -
    - - - - - - ``` - -3. To activate the iFrame, you should include the following code as a script in your html app. - - ```jsx - - ``` - - You’ll notice two things used above - **`applicationIdentifier`** and **`subscriberID`**. - - An application identifier is a public key used to identify your application. You can get your own application identifier from the **[Novu dashboard settings](https://web.novu.co/settings?utm_campaign=docs-quick-iframe)**. - - And subscribers are users to which notifications will be sent. They are identified by a **`subscriberID`** which you can also find in the **[Novu subscribers dashboard](https://web.novu.co/subscribers?utm_campaign=docs-quick-iframe)**. - - -4. Now open your html application, you should see something like this: - - - -If you have replaced the `applicationIdentifier` and `subscriberID`, when you will click the bell, you will see the following: - - - -To learn how to design and modify your iFrame component, feel free to visit the more in-depth [iFrame section](notification-center/client/iframe). - -## Trigger a notification - -We can trigger a notification by simply clicking on the `Get Snippet` button - - - -Switch to `Run a Test` option - - - -Change the `subscriberId` that we have created (`123`) - - - -Click on `Run Trigger` - - - -This will take the workflow we created above with the identifier `quickstart` and send a notification to the subscriber with `subscriberId` of `123`. - -Now we will open our web application and see a red dot next to the bell icon, this indicate that we have an unseen notification in our feed. - -When we will click on it, we will see our notification. - - - -## Conclusion - -Great job! If you’ve reached this point, you should now have successfully set up the notification center, created a subscriber, a workflow, triggered a notification and saw it in your web application using iFrame embed. - -If you want to learn more about iFrame embed component, please visit [this section](/notification-center/client/iframe) of the documentation. \ No newline at end of file diff --git a/quickstarts/java.mdx b/quickstarts/java.mdx deleted file mode 100644 index e646b0bc..00000000 --- a/quickstarts/java.mdx +++ /dev/null @@ -1,341 +0,0 @@ ---- -title: 'Java quickstart' -description: 'Learn how to use Novu to quickly send multi-channel (SMS, Email, Chat, Push) notifications with Java.' -icon: 'java' ---- - -import SupportedChannelsSnippet from "/snippets/supported-channels.mdx"; - -## Java Quickstart - -Learn how to integrate Novu into your Java project on the fly and send notifications across different channels (SMS, Email, Chat, Push). - -In this Quickstart, you will learn how to: - -- [Install and Set up the Novu Java SDK.](#install-and-set-up-the-novu-java-sdk-in-your-project) -- [Configure a Notification Channel Provider.](#set-up-a-channel-provider) -- [Set up a Notification Workflow.](#create-a-notification-workflow) -- [Create a Subscriber and update a Subscriber's information.](#create-a-subscriber-and-update-a-subscribers-information) -- [Send your first Notification to a Subscriber.](#trigger-a-notification) -- [Send Notifications via Topics.](#topics) - -Let's get started! :muscle: - -## Requirements - -To be able to use this Quickstart, you would need to have the following: - -- A Novu account, if you don't have one yet, [sign up](https://web.novu.co?utm_campaign=docs-gs-quick-java) for free. -- A working Java development environment with JDK 17 and Springboot Framework of 3.1.2+. - -The completed code for this quick start is available on GitHub. Check it out [here](https://github.com/novuhq/novu-java-quickstart). - -## Install and Set up the Novu Java SDK in your project - -First, you need to import the library into your application. -If you use Maven, add the dependency to the `pom.xml` file; - -```xml - - co.novu - novu-java - {use-latest-version} - -``` - -The latest version can be found [on GitHub.](https://github.com/novuhq/novu-java#installation) - -If you use Gradle, add the dependency to the `build.gradle` file; - -```groovy -dependencies { - implementation 'co.novu:novu-java:{use-latest-version}' -} -``` - -Sync your project, and you should have the artifacts downloaded. - -To use the SDK, you need to have your API key handy, it can be gotten from [settings page](https://web.novu.co/settings?utm_campaign=docs-quick-java) of the web dashboard. - -To initialize the library, you can create an instance of `Novu.java` class using any of the following constructors: - -```java -//Using the API Key only -String apiKey = "INSERT_API_KEY_HERE"; - Novu novu = new Novu(apiKey); -``` - -```java -//Using the API Key plus an instance of NovuConfig -String apiKey = "INSERT_API_KEY_HERE"; - NovuConfig novuConfig = new NovuConfig(apiKey); - Novu novu = new Novu(novuConfig); -``` - -Once that is done successfully, head over to the [web dashboard](https://web.novu.co?utm_campaign=docs-quick-java) for the next steps. - -## Set up a Channel Provider - -A channel provider is a service that provides one or more notification functionality such as sending an email, SMS, push notification, etc. Our [integration store](https://web.novu.co/integrations?utm_campaign=docs-quick-java) includes four channels: Email, SMS, Chat, and Push. These channels have multiple providers associated with them. - -| Channel | Providers | -| ------- | ------------------------------------------------------------------- | -| Email | MailGun, Mandrill, MailJet, Amazon SES, Sendgrid, Postmark, Netcore | -| SMS | Twilio, Amazon SNS, Plivo, SMS, SMSCentral, Kannel, Infobip, Termii | -| Chat | Mattermost, Slack, Microsoft Teams, Discord | -| Push | FCM, APNS, Expo | - -Only one provider can be **active** per **channel**. Connect any of your favorite providers to get started. The email channel comes with Novu's email provider, which is active by default and includes 300 credits. - -## Create a Notification Workflow - -A notification workflow is the blueprint for the notifications that will be sent. It holds the entire flow of messages sent to the subscriber. This is where all the different channels are tied together under a single entity. - -The workflow includes the following: - -- Notification workflow name and Identifier. -- Channel tailored content: - - - - -Proper authorization needs to be set for the Chat channel for subscribers. - - -Please proceed to create a notification workflow. - -1. Click **Workflows** on the left sidebar of your Novu dashboard. -2. Click the **Create Workflow** button on the top right. - - -3. The name of a new notification workflow is currently **Untitled**, rename it to a more suitable title. - - -4. Select **Email** as the channel you want to add, by dragging it from the right sidebar. - - -5. Click on the **Email** in the workflow and edit it as per this image. Don't forget to add the fields in the editor which is supposed to be updated with dynamic values that will be sent when calling the API. - - -6. Also, add the variables in the **Variables** section in the test tab and try testing it by sending the email to your email address using the **Send Test Email** button at the bottom right. - - - -You should get an email within seconds! :ok_hand: - -Great, you have successfully sent your first notification via the Novu dashboard! Now, let's take a step further to trigger notifications via code. - -## Create and Update a Subscriber's Information - -The recipients of a triggered notification are called subscribers. - -Click **Subscribers** on the left sidebar of the Novu dashboard to see all subscribers. By default, the dashboard will display a subscriber, as you were added automatically during sign-up. - - - - -Now, let's create a subscriber on Novu. Copy and paste the following code to do so: - -```java -String apiKey = "API_KEY"; - Novu novu = new Novu(apiKey); - - SubscriberRequest subscriberRequest = new SubscriberRequest(); - subscriberRequest.setEmail("email"); - subscriberRequest.setFirstName("fName"); - subscriberRequest.setLastName("lName"); - subscriberRequest.setPhone("phone"); - subscriberRequest.setAvatar("avatar"); - subscriberRequest.setSubscriberId("subscriberId"); - try { - return novu.createSubscriber(subscriberRequest); - }catch (Exception e){ - log.error("Error Creating Subscriber", e); - } - - return null; -``` - -When you run this code snippet, you should see the subscriber on your Novu dashboard. - -Now, let's assume you want to modify the email address of the subscriber you just created, you can do that with the following code: - -```java -String apiKey = "API_KEY"; - Novu novu = new Novu(apiKey); - - UpdateSubscriberRequest updateSubscriberRequest = new UpdateSubscriberRequest(); - updateSubscriberRequest.setEmail("updatedEmail"); - updateSubscriberRequest.setFirstName("fName"); - updateSubscriberRequest.setLastName("lName"); - updateSubscriberRequest.setPhone("phone"); - updateSubscriberRequest.setAvatar("avatar"); - - String subscriberId = "subscriberId"; - - try { - return novu.updateSubscriber(updateSubscriberRequest, subscriberId); - }catch (Exception e){ - log.error("Error updating Subscriber", e); - } - - return null; -``` - -Other valid fields that can be updated are `phone`, `avatar`, and `data`. The `data` field can accept an Object or a Map with the info you want to attach to the subscriber. - - -To create all of your subscribers, you need to programmatically add them to Novu. - - -## Trigger a Notification - -Copy and paste the following code into your app to trigger a notification to a Subscriber: - -```java -String apiKey = "API_KEY"; - Novu novu = new Novu(apiKey); - - SubscriberRequest subscriberRequest = new SubscriberRequest(); - subscriberRequest.setSubscriberId("subscriberId"); - subscriberRequest.setEmail("email;"); - subscriberRequest.setFirstName("fName"); - subscriberRequest.setLastName("lName"); - - TriggerEventRequest triggerEventRequest = new TriggerEventRequest(); - triggerEventRequest.setName("name"); - triggerEventRequest.setTo(subscriberRequest); - triggerEventRequest.setPayload(Collections.singletonMap("customVariables", "Hello")); - - - try { - return novu.triggerEvent(triggerEventRequest); - }catch (Exception e){ - log.error("Error triggering event", e); - } - - return null; -``` - -Before running the code, make sure you understand the following: - -- The value of `payload` is an array of the data that you want to be dynamically injected into the notification workflow content. -- The value of `subscriberId` is the ID of the subscriber on Novu. Replace `12345` with your subscriber ID. - -Run the code to trigger a notification! - -## Topics - -Novu provides a simple API that offers an easy interface for triggering notifications to multiple subscribers at once. This API is called **Topics** and allows users to manage their bulk notifications without having to implement complex loops. A topic is identified by a custom key that is provided by the user, and this key will be the identifier used in the Topics API. - - -The topic key should be unique and can't be changed once chosen. Novu also caters for key uniqueness behind the scenes. - - -A topic can have multiple subscribers who will receive a notification whenever a message is sent to the topic. - -## Create a Topic - -Copy and paste the following code into your app to create a topic: - -```java -String apiKey = "API_KEY"; -Novu novu = new Novu(apiKey); - -TopicRequest createTopicRequest = new TopicRequest(); - createTopicRequest.setKey("key"); - createTopicRequest.setName("name"); - try { - return novu.createTopic(createTopicRequest); - }catch (Exception e){ - log.error("Error creating Topic", e); - } - - return null; -``` - -Before running the code, make sure you understand the following: - -- When creating a `key`, ensure it is unique and accurately identifies the topic. Document naming conventions and communicate them to team members to avoid confusion and ensure a smooth workflow. -- The value of `name` should be a descriptive topic name. - -## Add Subscribers to a Topic - -Copy and paste the following code into your app to add subscribers a topic: - -```java -String apiKey = "API_KEY"; -Novu novu = new Novu(apiKey); - - String topicKey = "key"; - SubscriberAdditionRequest requestBody = new SubscriberAdditionRequest(); - requestBody.setSubscribers(Collections.singletonList("aSubscriberId")); - - try { - return novu.addSubscriberToTopic(requestBody,topicKey); - }catch (Exception e){ - log.error("Error Adding Subscriber To Topic", e); - } - - return null; -``` - -On the other hand, if you want to remove subscribers from a topic, do the following: - -```java -String apiKey = "API_KEY"; -Novu novu = new Novu(apiKey); - - - String topicKey = "key"; - SubscriberAdditionRequest requestBody = new SubscriberAdditionRequest(); - requestBody.setSubscribers(Collections.singletonList("aSubscriberId")); - - try { - return novu.removeSubscriberFromTopic(requestBody, topicKey); - }catch (Exception e){ - log.error("Error Removing Subscriber From Topic", e); - } - - return null; -``` - -## Send a Notification to a Topic - -Thanks to the topics feature, it is possible to trigger a notification to all subscribers assigned to a topic. This helps avoid listing all subscriber identifiers in the `to` field of the notification trigger. - -To trigger a notification to all subscribers of a topic, copy and paste the code below: - -```java -String apiKey = "API_KEY"; - Novu novu = new Novu(apiKey); - - - Topic topic = new Topic(); - topic.setType("Topic"); - topic.setTopicKey("topicKey"); - - TriggerEventRequest triggerEventRequest = new TriggerEventRequest(); - triggerEventRequest.setName("name"); - triggerEventRequest.setTo(topic); - triggerEventRequest.setPayload(Collections.singletonMap("customVariables", "Hello")); - - - try { - return novu.triggerEvent(triggerEventRequest); - }catch (Exception e){ - log.error("Error sending notification to topic", e); - } - - return null; -``` - -## Next Steps - -Great job! :clap: If you've reached this point, you should have successfully created a subscriber, notification workflow, configured a channel provider, triggered a single notification, created a topic, added a subscriber to a topic in your application. - -To learn more about notifications and explore Novu's features and capabilities, check out: - -- [Novu Java SDK](https://github.com/novuhq/novu-java) - Dive deeper into the SDK and explore a lot of features. -- [Novu Digest Engine](/guides/add-digest-to-inapp-notifications) - Learn how to aggregate multiple trigger events into a single message and deliver it to the subscriber. -- [Novu Notification Center](/getting-started/introduction) - Learn how to integrate a rich, ready-to-use real-time UI notification center into your app. \ No newline at end of file diff --git a/quickstarts/kotlin.mdx b/quickstarts/kotlin.mdx deleted file mode 100644 index 4c31615b..00000000 --- a/quickstarts/kotlin.mdx +++ /dev/null @@ -1,293 +0,0 @@ ---- -title: 'Kotlin quickstart' -description: 'Learn how to use Novu to quickly send multi-channel (SMS, Email, Chat, Push) notifications with Kotlin.' -icon: 'kickstarter' ---- - -import SupportedChannelsSnippet from "/snippets/supported-channels.mdx"; - -Learn how to integrate Novu into your Kotlin project on the fly and send notifications across different channels (SMS, Email, Chat, Push). - -Let's get started! 💪 - -## Requirements - -To be able to use this Quickstart, you would need to have the following: - -- A Novu account, if you don't have one yet, [sign up](https://web.novu.co/?utm_campaign=docs-gs-quick-kotlin) for free. -- A working Kotlin project running version 1.6.0+ and Kotlin compiler 1.8.0+. - -The completed code for this quick start is available on GitHub. Check it out [here](https://github.com/novuhq/novu-kotlin-quickstart). - -## Install and Set up the Novu Kotlin SDK in your project - -First, you need to import the library into your application. - -If you use Maven, add the dependency to the `pom.xml` file; - -```xml - - co.novu - novu-kotlin - {use-latest-version} - -``` - -The latest version can be found [on GitHub.](https://github.com/novuhq/novu-kotlin#installation) - -If you use Gradle, add the dependency to the `build.gradle` file; - -```groovy -implementation 'co.novu:novu-kotlin:{use-latest-version}' //Groovy - -implementation ("co.novu:novu-kotlin:{use-latest-version}") //Kotlin -``` - -Sync your project, and you should have the artifacts downloaded. - -To use the SDK, you need to have your API key handy, it can be gotten from the [settings page](https://web.novu.co/settings?utm_campaign=docs-quick-kotlin) of the web portal. - -To initialize the library, you can create an instance of `Novu.kt` class using any of the following constructors: - -```kotlin -//Using the API Key only -val novu = Novu(apiKey = "API_KEY") - -``` - -```kotlin -//Using the API Key plus an instance of NovuConfig -val novuConfig = NovuConfig(backendUrl = "URL") -val novu = Novu(apiKey = "API_KEY", config = novuConfig) - -``` - -Once that is done successfully, head over to the [web portal](https://web.novu.co/?utm_campaign=docs-quick-kotlin) for the next steps. - -## Set up a Channel Provider - -A channel provider is a service that provides one or more notification functionality such as sending an email, SMS, push notification, etc. Our [integration store](https://web.novu.co/integrations?utm_campaign=docs-quick-kotlin) includes four channels: Email, SMS, Chat, and Push. These channels have multiple providers associated with them. - -| Channel | Providers | -| --- | --- | -| Email | MailGun, Mandrill, MailJet, Amazon SES, Sendgrid, Postmark, Netcore | -| SMS | Twilio, Amazon SNS, Plivo, SMS, SMSCentral, Kannel, Infobip, Termii | -| Chat | Mattermost, Slack, Microsoft Teams, Discord | -| Push | FCM, APNS, Expo | - -Only one provider can be **active** per **channel**. Connect any of your favourite providers to get started. The email channel comes with Novu's email provider, which is active by default and includes 300 credits. - -## Create a Workflow - -A workflow is a blueprint for the notifications that will be sent. It holds the entire flow of messages sent to the subscriber. This is where all the different channels are tied together under a single entity. - -The workflow includes the following: - -- Workflow name and Identifier. -- Channel-tailored content: - - - - - -Proper authorization needs to be set for the Chat channel for subscribers. - - -Please proceed to create a workflow. - -1. Click **Workflows** on the left sidebar of your Novu dashboard. -2. Click the **Create Workflow** button on the top right. - -3. The name of the new workflow is currently Untitled, rename it to a more suitable title. - -4. Select Email as the channel you want to add, by dragging it from the right sidebar. - -5. Click on the Email in the workflow and edit it as per this image. Don’t forget to add the fields in the editor which is supposed to be updated with dynamic values that will be sent when calling the API. - -6. Also, add the variables in the Variables section in the test tab and try testing it by sending the email to your email address using the Send Test Email button at the bottom right. - -You should get an email within seconds! 👌 - -Great, you have successfully sent your first notification via the Novu dashboard! Now, let’s take a step further to trigger notifications via code. - -## Create a Subscriber and Update a Subscriber's information - -The recipients of a triggered notification are called subscribers. - -Click **Subscribers** on the left sidebar of the Novu dashboard to see all subscribers. By default, the dashboard will display a subscriber, as you were added automatically during sign-up. - -Now, let's create a subscriber on Novu. Copy and paste the following code to do so: - -```kotlin -suspend fun createSubscriber(): ResponseWrapper? { - val novu = Novu(apiKey = "API_KEY") - val subscriberRequest = SubscriberRequest( - email = "email@sample.com", - firstName = "John",//optional - lastName = "Doe",//optional - phone = "123456789",//optional - avatar = "sample-avatar",//optional - subscriberId = "12345"//optional - ) - return CoroutineScope(Dispatchers.IO).async { - novu.createSubscriber(subscriberRequest) - }.await() - } - -``` - -When you run this code snippet, you should see the subscriber on your Novu dashboard. 👤 - -Now, let's assume you want to modify the email address of the Subscriber you just created, you can do that with the following code: - -```kotlin -suspend fun updateSubscriber(): ResponseWrapper? { - val novu = Novu(apiKey = "API_KEY") - val subscriberRequest = UpdateSubscriberRequest( - email = "validemail@sample.com", - firstName = "John",//optional - lastName = "Doe",//optional - phone = "123456789",//optional - avatar = "sample-avatar"//optional - ) - val subscriberId = "12345" - return CoroutineScope(Dispatchers.IO).async { - novu.updateSubscriber(subscriberId, subscriberRequest) - }.await() - } - -``` - -Other valid fields that can be updated are `phone`, `avatar`, and `data`. The `data` field can accept an Object or a Map with the info you want to attach to the subscriber. - - - -To create all of your subscribers, you need to programmatically add them to Novu. - - -## Trigger a Notification - -Copy and paste the following code into your app to trigger a notification to a Subscriber: - -```kotlin -suspend fun triggerNotification(): Any? { - val novu = Novu(apiKey = "API_KEY") - val triggerEventRequest = TriggerEventRequest( - name = "test", - to = SubscriberRequest( - subscriberId = "12345", - email = "email@email.com", - firstName = "John", - lastName = "Doe" - ), - payload = mapOf("customVariables" to "Hello") - ) - - return CoroutineScope(Dispatchers.IO).async { - novu.trigger(triggerEventRequest) - }.await() - } - -``` - -Before running the code, make sure you understand the following: - -- The value of `payload` is an array of the data that you want to be dynamically injected into the workflow content. -- The value of `subscriberId` is the ID of the subscriber on Novu. Replace `12345` with your subscriber ID. - -Run the code to trigger a notification! 📧 - -## Topics - -Novu provides a simple API that offers an easy interface for triggering notifications to multiple subscribers at once. This API is called **Topics** and allows users to manage their bulk notifications without having to implement complex loops. A topic is identified by a custom key that is provided by the user, and this key will be the identifier used in the Topics API. - - - -The topic key should be unique and can't be changed once chosen. Novu also caters for key uniqueness behind the scenes. - - -A topic can have multiple subscribers who will receive a notification whenever a message is sent to the topic. - -## Create a Topic - -Copy and paste the following code into your app to create a topic: - -```kotlin -suspend fun createTopic(): Any? { - val novu = Novu(apiKey = "API_KEY") - val createTopicRequest = CreateTopicRequest( - key = "key", - name = "name" - ) - return CoroutineScope(Dispatchers.IO).async { - novu.createTopic(createTopicRequest) - }.await() - } - -``` - -Before running the code, make sure you understand the following: - -- When creating a `key`, ensure it is unique and accurately identifies the topic. Document naming conventions and communicate them to team members to avoid confusion and ensure a smooth workflow. -- The value of `name` should be a descriptive topic name. - -## Add subscribers to a Topic - -Copy and paste the following code into your app to add subscribers a topic: - -```kotlin -suspend fun addSubscriberToTopic(): Any? { - val novu = Novu(apiKey = "API_KEY") - val topicKey = "key" - val requestBody = SubscriberList(listOf("name")) - return CoroutineScope(Dispatchers.IO).async { - novu.addSubscribers(topicKey, requestBody) - }.await() - } - -``` - -On the other hand, if you want to remove subscribers from a topic, do the following: - -```kotlin -suspend fun removeSubscriberFromTopic(): Any? { - val novu = Novu(apiKey = "API_KEY") - val topicKey = "key" - val requestBody = SubscriberList(listOf("name")) - return CoroutineScope(Dispatchers.IO).async { - novu.removeSubscriber(topicKey, requestBody) - }.await() - } - -``` - -## Sending a Notification to a Topic - -Thanks to the topics feature, it is possible to trigger a notification to all subscribers assigned to a topic. This helps avoid listing all subscriber identifiers in the `to` field of the notification trigger. - -To trigger a notification to all subscribers of a topic, copy and paste the code below: - -```kotlin -suspend fun triggerNotificationToTopic(): Any? { - val novu = Novu(apiKey = "NOVU_API_KEY") - val triggerEventRequest = TriggerEventRequest( - name = "test", - to = listOf( - Topic( - type = "Topic", - topicKey = "posts:comment:12345" - ) - ), - payload = mapOf("customVariables" to "Hello") - ) - - return CoroutineScope(Dispatchers.IO).async { - novu.trigger(triggerEventRequest) - }.await() - } -``` - -## Conclusion - -Great job! 👏 -If you've reached this point, you should have successfully created a subscriber, workflow, configured a channel provider, triggered a single notification, created a topic, and added a subscriber to a topic in your application. diff --git a/quickstarts/nextjs.mdx b/quickstarts/nextjs.mdx deleted file mode 100644 index 605a9271..00000000 --- a/quickstarts/nextjs.mdx +++ /dev/null @@ -1,326 +0,0 @@ ---- -title: 'NextJS' -description: 'Learn how to use Novu to quickly send multi-channel (SMS, Email, Chat, Push) notifications with NextJS.' -icon: 'N' ---- - -This quick start guide is written to help you integrate Novu into your NextJS app in no time. Novu is an open-source notifications infrastructure that you can use in your app to deliver rich notifications across channels like sms, email, in-app, push, chat, etc. In this guide, you’ll learn how to integrate email notifications into your NextJS app. - -You can find the code for this app [here](https://github.com/novuhq/nextjs-quickstart/) - -## Prerequisites - -- Node.js installed on your development machine (to create a NextJS app). -- A Novu account. If you don't have one, sign up for free [here](https://web.novu.co/workflows?utm_campaign=docs-quick-nextjs). - -## Create a NextJS app to get started - -The first step here would be to create a NextJS app. We will play with Novu in this app later. To get started, open your terminal and create a NextJS app using the following command: - -```bash -npx create-next-app@latest - -``` - -This will create a NextJS app and now we can add Novu to our project. Open the app using a code editor of your choice. The benefit of using NextJS is that it provides native support for routing, making it effortless to handle navigation and create dynamic pages in your application. - -Next.js uses a file-based routing system, where each page in your application is represented by a corresponding file in the `pages` directory. - -By creating a new file in the `pages` directory, such as `about.js`, we automatically create a route `/about` in our application. Next.js handles the routing logic behind the scenes, ensuring that when a user accesses `/about`, the corresponding page component is rendered. - -In our app, we’ll create a route that will send an email notification to a specified email id. To do this, we need two things: - -- Install Novu and connect it to our app -- Create the route and define a function in that route - -## Installing Novu and connecting it to the app - -To install Novu, simply open your terminal and issue the following command: - -```bash -npm install @novu/node - -``` - -After installing it, we need to connect our app with our Novu account using the Novu API key. Simply log onto the [Novu web dashboard](https://web.novu.co?utm_campaign=docs-quick-nextjs/) and from the settings there, obtain your API key. We’ll use it to connect our app to our Novu account. - -Now, we need to connect it to our app. To do so, the first step is to import Novu into our app. Importing Novu will give us the ability to interact with the Novu API. To import Novu into our app, use the following code in a separate file in our app: - -```jsx -import { Novu } from '@novu/node'; - -// replace with your actual API key -const novu = new Novu(); -``` - -Novu lets us send notifications across different channels like email, in-app, chat, SMS, etc, and for each channel, one can use a plethora of providers. You just need to set up a provider for the channel you want to use in Novu: - -| Channel | Providers | | -| --- | --- | --- | -| Email | MailGun, Mandrill, MailJet, Amazon SES, Sendgrid, Postmark, Netcore | | -| SMS | Twilio, Amazon SNS, Plivo, SMS, SMSCentral, Kannel, Infobip, Termii | | -| Chat | Mattermost, Slack, Microsoft Teams, Discord | | -| Push | FCM, APNS, Expo | | - -You can see all this in our [integrations store](https://web.novu.co/integrations?utm_campaign=docs-quick-nextjs) and set it up there. - -For each channel, there can be only one provider active at a time. Although the exact setup process varies from provider to provider, the general flow is signing up for a provider, getting an API key from its portal, and plugging it into the Novu web portal. - -Once having integrated a provider, we need a workflow to send notifications. One can have dynamic data in this workflow if they so choose. - -In our case, we’ll have dynamic data and whatever we send as a description will be sent as an email notification. Following are the steps to create a workflow. - -## Creating a workflow - -1. Click "Workflows” on the left sidebar of your Novu dashboard. -2. Click the “Create Workflow” button on the top right. - -3. The name of the new workflow is currently "Untitled." Rename it to a more suitable title. - -4. Select "Email" as the channel you want to add, by dragging it from the right sidebar: - -5. Click on the ‘Email’ in the workflow and edit it as per this image. Don’t forget to add the fields in the editor which is supposed to be updated with dynamic values that we’ll send when calling the API. - -6. Also, add the variables in the ‘variables’ section in the test tab and try testing it by sending the email to your email id using the ‘send test email’ button on the bottom right. - - -Now, we’ve successfully sent the test email and just need to do this from our app. - -## Subscribers - -In Novu, entities that are supposed to receive notifications are called subscribers. You can see all the subscribers in the ‘subscribers’ tab in the left menu on the Novu web portal. There would be one subscriber by default, which is you. This was created when you signed up for Novu. - -In real-world scenarios, the `subscriber id` would be a unique id generated automatically by the database. So for our demo purpose, we’re using a simple subscriber id of ‘1234567890’ in our app. - -We’ll first create a subscriber using the following code: - -```jsx -import { Novu } from '@novu/node'; - -const novu = new Novu(); - -export async function sendEmail(email, description) { - await novu.subscribers.identify('1234567890', { - email: email, - firstName: "Subscriber", - }); -} - -``` - -Here, we’re creating a subscriber with the subscriber id of `1234567890` and now we’ll send a notification to the subscriber with this very subscriber id by triggering an email notification like follows: - -```jsx -import { Novu } from '@novu/node'; - -const novu = new Novu(); - -export async function sendEmail(email, description) { - await novu.subscribers.identify('1234567890', { - email: email, - firstName: "Subscriber", - }); - await novu.trigger('email-quickstart', { - to: { - subscriberId: '1234567890', - email: email - }, - payload: { - email: email, - description: description - } - }); -} - -``` - -We’ve exported this function so that we can use this in the route that we’ll now create. - -## Create the route and define a function in that route - -Now, create a `pages` directory in the root of the project and create a file in it. Give it a name and remember that it will automatically become a route. - -In our case, we’re creating a directory called `api` in our pages directory, and inside `api` we’re creating a file called `sub.js`. In this case, our path will be: `http://localhost:3000/api/sub` - -In this file, we simply need to define a function that will handle a POST request to our API. It’ll extract the ‘description’ and ‘email’ variables from the ‘request’ body that will be generated every time we make a POST request and call the function to send an email notification with those plugged in. - -We’d used these variables in the workflow we had created in the Novu dashboard and also specified the same in the trigger function above. - -The function in our route is quite simple and looks like this: - -```jsx -import { sendEmail } from '@/app/utils/novu'; - -export default async function subscribe(req, res) { - try { - if (req.method === 'POST') { - const { email, description } = req.body; - await sendEmail(email, description); - await res.status(200).json({ message: 'email working' }); - } - } catch (error) { - console.log(error); - res.status(405).json({ message: 'not working' }); - } -} - -``` - -We can now start our local server and test our backend app on Postman. To start the local server, use the following command: - -```bash -npm run dev - -``` - -Now, open Postman and send a POST request to the route you created earlier. The exact route depends on the file structure you have followed in the pages directory, but in our case, it is - `http://localhost:3000/api/sub` - -Also, make sure that in the body, you’re passing the two variables we’re extracting above, namely - email and description, as follows: - -> In place of youremail@gmail.com, use your actual email and once you send it, you should see the ‘message: email working’ on the bottom as in the image above. -> - -This means that the email notification was sent successfully. Now go to your inbox and you should see an email notification like the following: - - -## Topics - -Novu simplifies the process of triggering notifications to multiple subscribers with an API called "Topics". By utilizing the Topics API, you can effortlessly manage bulk notifications easily. - -Each topic is uniquely identified by a custom key specified by the user, serving as the primary identifier for interacting with the Topics API. - -This intuitive approach streamlines notifications management, empowering users to focus on delivering targeted messages to their subscribers without the hassle of intricate implementation details. - -> Make sure that you use a unique key for a Topic. Keys once used, can’t be changed later! -> - -You have the flexibility to assign a descriptive name to a topic. Unlike the topic key, this name does not require uniqueness and can be modified using the provided API. - -A topic can have multiple subscribers associated with it. These subscribers will receive notifications whenever a notification is dispatched to the respective topic. - -## Create a topic - -You can create a topic using two entities - `key` and `name`. Keys are unique identifiers for topics and a name is just something you assign to a topic for convenience. - -```jsx -import { Novu } from '@novu/node'; - -export default async function createTopic(req, res) { - try { - const novu = new Novu(""); - if (req.method === 'POST') { - const { key, name } = req.body; - const result = await novu.topics.create({ key, name }); - res.status(201).json(result.data); - } - } catch (error) { - console.log(error); - res.status(500).json({ message: error.message }); - } -} - -``` - -This is a simple function that calls the **`create`** method on the **`topics`** property of the **`novu`**instance. This method creates a new topic in the Novu system using the provided **`key`** and **`name`** values. - -If you test this on your local machine, you should get something like this: - - -> Note how the return object contains the key I sent from my request body. That signals successful creation! -> - -## Add subscriber to a Topic - -The code for adding a subscriber to a previously created topic is as follows: - - - -You can only add those subscribers to a topic that you've already created. You can see all the subscribers in the [Novu web dashboard](https://web.novu.co/subscribers)?utm_campaign=docs-quick-nextjs - - -```jsx -import { Novu } from '@novu/node'; - -export default async function addSub(req, res) { - try { - const novu = new Novu(""); - if (req.method === 'POST') { - // Get the subscriber ID from the request body - const subscriberId = req.body.subscriberId; - // Get the topic key from the request body - const topicKey = req.body.topicKey; - // Call Novu SDK to add the subscriber to the topic - const result = await novu.topics.addSubscribers(topicKey, { - subscribers: [subscriberId], - }); - // Return the result as JSON response - res.status(200).json(result.data); - } - } catch (error) { - console.log(error); - res.status(500).json({ message: error.message }); - } -} - -``` - -If you see the code above closely, you’ll see that we first establish a connection to Novu using the Novu API key. - -Then we extract `subscriberID` and topicKey from the request body and call the **`addSubscribers`** method on the **`topics`** property of the **`novu`** instance, passing the topic key and an object with an array of subscribers. - -This adds the subscriber with the `subscriberID` we’d passed to the array of subscribers, which in the above case contains just one subscriber. - -If you check this on Postman, the returned array will contain the `subscriberID` that we had passed in the request body, signalling that it was added to the topic successfully. - -If, on the other hand, you find the passed `subscriberId` in the `notFound` array inside the `failed` object, it means the subscriber wasn't added to the topic. - -You can read more about it [here](https://docs.novu.co/platform/topics/#subscribers-management-in-a-topic). - -The image below shows the case where the subscriber has been added successfully: - - -After creating a topic and adding subscribers to the topic, we’ll now proceed to send a notification to all subscribers (we only have one subscriber in our topic though) of a topic. This is what Topics are used for - sending bulk notifications. - -## Sending notifications to a topic - -Sending notifications to a topic is not a complex task. You need to extract the topic key to which you want to send notifications and trigger Novu’s method on that topic key with the message in the payload: - -```jsx -import { Novu } from '@novu/node'; - -export default async function sendNotifToSub(req, res) { - try { - const novu = new Novu(""); - if (req.method === 'POST') { - // Get the topic key from the request body - const topicKey = req.body.topicKey; - const email = req.body.email; - const description = req.body.description; - - // Call Novu SDK to trigger a notification to the topic subscribers - const result = await novu.trigger('email-quickstart', { - to: [{ type: 'Topic', topicKey: topicKey }], - payload: { - email: email, - description: description, - }, - }); - // Return the result as JSON response - res.json(result.data); - } - } catch (error) { - console.log(error); - res.status(500).json({ message: error.message }); - } -} - -``` - -Testing this on Postman should give you something like this: - - -If you see this, then it means the notification was sent successfully, as can be seen in my inbox down below: - - -## Conclusion -Great job! If you've reached this point, you should now have successfully created a subscriber, and workflow, configured a channel provider, triggered a single notification, and sent an email notification. diff --git a/quickstarts/nodejs.mdx b/quickstarts/nodejs.mdx deleted file mode 100644 index 153ed81b..00000000 --- a/quickstarts/nodejs.mdx +++ /dev/null @@ -1,214 +0,0 @@ ---- -title: 'NodeJS quickstart' -description: 'Learn how to use Novu to quickly send multi-channel (SMS, Email, Chat, Push) notifications with Node.js.' -icon: 'node' ---- - -import SupportedChannelsSnippet from "/snippets/supported-channels.mdx"; - -Welcome to the Node.js Quickstart guide for Novu, a powerful notification service that enables you to send multi-channel (SMS, Email, Chat, Push) notifications from your Node.js applications. In this Quickstart, you'll learn how to seamlessly integrate Novu into your app and perform various essential tasks. Let's get started! - -## Prerequisites - -Before diving into the Quickstart, make sure you have the following: - -- Node.js installed on your development machine. -- A Novu account. If you don't have one, sign up for free at [the web dashboard](https://web.novu.co/?utm_campaign=docs-quick-nodejs) - -You can also [view the completed code](https://github.com/novuhq/nodejs-sdk-quickstart) of this quick start in a GitHub repo. - -## Install and Set Up Novu in your Node.js App - -First, you must install the Novu package in your Node.js application. Open your terminal and run the following command: - -```bash -npm install @novu/node - -``` - -Once installed, you can import Novu into your app and initialize it using your Novu account credentials. This step establishes a connection between your app and the Novu notification service. - -```jsx -import { Novu } from '@novu/node'; - -const novu = new Novu(''); -``` - -Replace the `` value with the authentic key from the **API Key** section of your [Novu Dashboard](https://web.novu.co/settings?utm_campaign=docs-quick-nodejs). - -🔑 Note: Please do not hardcode your credentials in a file in production. Use environment variables instead. - -## Set Up A Channel Provider - -A channel provider is a service that provides one or more notification functionality such as sending an email, SMS, push notification etc. Our [integration store](https://web.novu.co/integrations?utm_campaign=docs-quick-nodejs) includes four channels: Email, SMS, Chat, and Push. These channels have multiple providers associated with them. - -| Channel | Providers | -| --- | --- | -| Email | MailGun, Mandrill, MailJet, Amazon SES, Sendgrid, Postmark, Netcore | -| SMS | Twilio, Amazon SNS, Plivo, SMS, SMSCentral, Kannel, Infobip, Termii | -| Chat | Mattermost, Slack, Microsoft Teams, Discord | -| Push | FCM, APNS, Expo | - -Only one provider can be **active** per **channel**. Connect any of your favorite providers to get started. The email channel comes with Novu's email provider, which is active by default and includes 300 credits. - -## Create a Workflow - -A workflow is the blueprint for the notifications that will be sent. It holds the entire flow of messages sent to the subscriber. This is where all the different channels are tied together under a single entity. - -The workflow includes the following: - -- Workflow name and Identifier -- Channel tailored content: - - - -Note: Proper authorization needs to be set for the Chat channel for subscribers. - -Please proceed to create a workflow. - -1. Click “Workflows” on the left sidebar of your Novu dashboard. -2. Click the “Create Workflow” button on the top right. -3. The name of the new workflow is currently "Untitled." Rename it to a more suitable title. -4. Select "Email" as the channel you want to add. - -5. Click on the recently added channel, fill the email subject and click “Update”. - -6. Click on the “Test” tab and send a test email to verify your workflow. - -You should get an email within seconds. Yaaay, you have successfully sent your first notification via the Novu dashboard! Now, let’s take it a step further to trigger notifications via code. - -## Create A Subscriber - -The recipients of a triggered notification are called subscribers. - -Click “Subscribers” on the left sidebar of the Novu dashboard to see all subscribers. By default, the dashboard will display a subscriber, as you were added automatically during sign-up. - -Now, let's create a subscriber on Novu. Copy and paste the following code to do so: - -```jsx -// Create a subscriber -const subscriberId = '7789'; // Replace this with a unique user ID that matches your database. -await novu.subscribers.identify(subscriberId, { - email: 'abc@gmail.com', // optional - firstName: 'John', // optional - lastName: 'Doe', // optional - phone: '', // optional - avatar: '', // optional - locale: '', // optional - data: { customKey1: 'customVal1', customKey2: 'customVal2' }, // optional -}); -``` - -Run the code in your terminal like so: - -```bash -node index.js - -``` - -You should see the subscriber on your Novu dashboard. - -I’d like to publicly announce that `abc@gmail.com` is a random unlikely email your users will have. To update this to an alternative email, you can call the `updateSubscriber` method like so: - -```jsx -// Update subscriber detail -await novu.subscribers.update('7789', { - // new email - email: 'validemail@gmail.com', // optional - // new phone - phone: '+19874567832', // optional -}); -``` - -Other valid fields that can be updated are `phone`, `avatar`, and `data` . The `data` field can accept an array of metadata that you want to attach to the subscriber. - -Note: To make all of your app users subscribers, you need to programmatically add them to Novu. - -## Trigger A Notification - -Copy and paste the following code into your app to trigger a notification: - -```jsx -const notificationWorkflowId = 'first-email'; - -novu.trigger(notificationWorkflowId, { - to: { - subscriberId: '7789', - }, - payload: {}, -}); -``` - -Before running the code, make sure you understand the following: - -- The value of `notificationWorkflowId` should be the workflow's trigger ID/slug. - -- The value of `payload` is an array of the data that you want to be dynamically injected into the workflow content. -- The value of `subscriberId` is the id of the subscriber on Novu. Replace `7789` with your subscriber ID. - -Run the code to trigger a notification! - -```bash -node index.js - -``` - -## Topics - -Novu provides a simple API that offers an easy interface for triggering notifications to multiple subscribers at once. This API is called "Topics" and allows users to manage their bulk notifications without having to implement complex loops. A topic is identified by a custom key that is provided by the user, and this key will be the identifier used in the Topics API. - -⚠️ DANGER: The topic key should be unique and can't be changed once chosen. Novu also safe guards for key uniqueness behind the scenes. - -Users can assign a name to a topic for descriptive purposes. This name does not need to be unique and can be changed using the API. - -A topic can have different subscribers, who will receive a notification whenever a notification is sent to the topic. - -## Create a topic - -Copy and paste the following code into your app to create a topic: - -```jsx -const result = await novu.topics.create({ - key: 'unique-topic-identifier', - name: 'descriptive-topic-name', -}); -``` - -Before running the code, make sure you understand the following: - -- When creating a `key`, ensure it is unique and accurately identifies the topic. Document naming conventions and communicate them to team members to avoid confusion and ensure a smooth workflow. -- The value of `name` should be a descriptive topic name. - -## Add Subscribers To A Topic - -Copy and paste the following code into your app to add subscribers to a topic. - -```jsx -const topicKey = ''; - -const response = await novu.topics.addSubscribers(topicKey, { - subscribers: ['subscriber-id-1', 'subscriber-id-2', ...], -}); -``` - -- To add multiple subscribers to a topic, simply separate their names with commas and add them to the array as part of the `subscribers` value. - -## Send Notification To A Topic - -Thanks to the topics feature, it is possible to trigger a notification to all subscribers assigned to a topic. This helps avoid listing all subscriber identifiers in the `to` field of the notification trigger. - -To trigger a notification to all subscribers of a topic, use Novu's API as follows: - -```jsx -const topicKey = ''; -const notificationTemplateId = ''; - -await novu.trigger(notificationTemplateId, { - to: [{ type: 'Topic', topicKey: topicKey }], - payload: {}, -}); -``` - -## Conclusion - -Great job! If you've reached this point, you should now have successfully created a subscriber, workflow, configured a channel provider, triggered a single notification, created a topic, added a subscriber to a topic and even triggered a notification to a topic in your application. diff --git a/quickstarts/overview.mdx b/quickstarts/overview.mdx deleted file mode 100644 index 6346a777..00000000 --- a/quickstarts/overview.mdx +++ /dev/null @@ -1,138 +0,0 @@ ---- -title: "Quickstarts" -description: "Learn how to use Novu to quickly send multi-channel (SMS, Email, Chat, Push) notifications with any language/framework of your choice." ---- - - - - Embed a real-time notification center component in your Vue application. - - - Embed a real-time notification center component in your React application. - - - Embed a real-time notification center component in your Angular application. - - - Embed a real-time notification center component in your VanillaJS - application. - - - - - - } - color="#16a34a" - href="/quickstarts/redwoodjs" - > - Embed a real-time notification center component in your RedwoodJS - application. - - - Embed a real-time notification center component in your iFrame application. - - - Connect a Node.js application to Novu. - - - } color="#16a34a" href="/quickstarts/php"> - Connect a PHP application to Novu. - - - } color="#F7DF1E" href="/quickstarts/python"> - Connect a Python application to Novu. - - - - - - - - - - - - } - color="#16a34a" - href="/quickstarts/kotlin" - > - Connect a Kotlin application to Novu. - - - - } - color="#16a34a" - href="/quickstarts/ruby" - -> - - Connect a Ruby application to Novu. - - - - } color="#16a34a" href="/quickstarts/.NET"> - Connect a .NET application to Novu. - - - } - color="#16a34a" - href="/quickstarts/nextjs" - > - Connect a NextJS application to Novu. - - - Connect a Go application to Novu. - - - } color="#EF652A" href="/quickstarts/java"> - Connect a Java application to Novu. - - diff --git a/quickstarts/php.mdx b/quickstarts/php.mdx deleted file mode 100644 index 5f030b30..00000000 --- a/quickstarts/php.mdx +++ /dev/null @@ -1,269 +0,0 @@ ---- -title: 'PHP quickstart' -description: 'Use Novu to send multi-channel (SMS, Email, Chat, Push) notifications from a PHP app.' -icon: 'php' ---- - -import SupportedChannelsSnippet from "/snippets/supported-channels.mdx"; - -Learn how to use Novu to quickly send multi-channel (SMS, Email, Chat, Push) notifications from a PHP app. - -## Requirements - -To follow the steps in this quickstart, you'll need: - -- A Novu account. [Sign up for free](http://web.novu.co/?utm_campaign=docs-gs-quick-php) if you don’t have one yet. -- A working PHP development environment with a PHP version of 7.2+. - -You can also [v](https://cloudinary.com/documentation/php_quickstart#view_the_completed_code)[iew the completed code](https://github.com/novuhq/novu-php-quickstart) of this quick start in a GitHub repo. - -## Install Novu PHP SDK - -The [PHP SDK](http://github.com/novuhq/novu-php) provides a fluent and expressive interface for interacting with Novu's API and managing notifications. - -Now install the Novu PHP SDK by running the following command in your terminal: - -```bash -composer require unicodeveloper/novu - -``` - -Otherwise, create a file named `composer.json` and add the following to it: - -```php -{ - "require": { - "unicodeveloper/novu": "^1.0" - } -} - -``` - -Next, install the SDK by running the following `composer` command: - -```bash -composer install - -``` - -## Initialize & Configure the Novu SDK - -Create a new file, `index.php` in your application and add the following code to it: - -```php -'; -$novu = new Novu($apiKey); -``` - -Replace the `$apiKey`’s value with the authentic key from the **API Key** section of your [Novu Dashboard](https://web.novu.co/settings?utm_campaign=docs-quick-php). - - - -Please do not hardcode your credentials in a file in production. Use environment variables instead. - - -## Set Up A Channel Provider - -A channel provider is a service that provides one or more notification functionality such as sending an email, SMS, push notification etc. Our [integration store](https://web.novu.co/integrations?utm_campaign=docs-quick-php) includes four channels: Email, SMS, Chat, and Push. These channels have multiple providers associated with them. - -| Channel | Providers | -| --- | --- | -| Email | MailGun, Mandrill, MailJet, Amazon SES, Sendgrid, Postmark, Netcore | -| SMS | Twilio, Amazon SNS, Plivo, SMS, SMSCentral, Kannel, Infobip, Termii | -| Chat | Mattermost, Slack, Microsoft Teams, Discord | -| Push | FCM, APNS, Expo | - -Only one provider can be **active** per **channel**. Connect any of your favourite providers to get started. The email channel comes with Novu's email provider, which is active by default and includes 300 credits. - -## Create A Workflow - -A workflow is a blueprint for the notifications that will be sent. It holds the entire flow of messages sent to the subscriber. This is where all the different channels are tied together under a single entity. - -The workflow includes the following: - -- Workflow name and Identifier -- Channel-tailored content: - - - - - -Proper authorization needs to be set for the Chat channel for subscribers. - - -Please proceed to create a workflow. - -1. Click **Workflows** on the left sidebar of your Novu dashboard. -2. Click the **Create Workflow** button on the top right. -3. The name of the new workflow is currently **Untitled.** Rename it to a more suitable title. -4. Select **Email** as the channel you want to add. - -5. Click on the recently added channel, fill the email subject and click “Update”. - -6. Click on the “Test” tab and send a test email to verify your workflow. - -You should get an email within seconds. Yaaay, you have successfully sent your first notification via the Novu dashboard! Now, let’s take it a step further to trigger notifications via code. - -## Create A Subscriber - -The recipients of a triggered notification are called subscribers. - -Click “Subscribers” on the left sidebar of the Novu dashboard to see all subscribers. By default, the dashboard will display a subscriber, as you were added automatically during sign-up. - -Now, let's create a subscriber on Novu. Copy and paste the following code to do so: -```php -// Create subscriber -$novu->createSubscriber([ - 'subscriberId' => '7789', // replace with system_internal_user_id - 'email' => 'abc@gmail.com', - 'firstName' => 'John', // optional - 'lastName' => 'Doe', // optional -])->toArray(); -``` - -Run the code in your terminal like so: - -```bash -php index.php - -``` - -You should see the subscriber on your Novu dashboard. - - - -I’d like to publicly announce that `abc@gmail.com` is a random unlikely email your users will have. To update this to an alternative email, you can call the `updateSubscriber` method like so: - -```php -// Update subscriber detail -$subscriberId = '7789'; -$novu->updateSubscriber($subscriberId, [ - 'email' => 'validemail@gmail.com', // replace with valid email - 'firstName' => '', // optional - 'lastName' => '', // optional -])->toArray(); -``` - -Other valid fields that can be updated are `phone`, `avatar`, and `data`. The `data` field can accept an array of metadata that you want to attach to the subscriber. - - - -To make all of your app users subscribers, you need to programmatically add them to Novu. - - -## Trigger A Notification - -Copy and paste the following code into your app to trigger a notification: - -```php -$novu->triggerEvent([ - 'name' => 'first-email', - 'payload' => ['first-name' => 'Adam'], - 'to' => [ - 'subscriberId' => '7789' - ] -])->toArray(); -``` - -Before running the code, make sure you understand the following: - -- The value of `name` should be the workflow’s trigger ID/slug. - -- The value of `payload` is an array of the data that you want to be dynamically injected into the workflow content. -- The value of `subscriberId` is the id of the subscriber on Novu. Replace `7789` with your subscriber ID. - -Run the code to trigger a notification! - -```php -php index.php - -``` - -Next, we’ll learn how to send notifications to different groups of subscribers easily via **Topics.** - -## Topics - -Novu provides a simple API that offers an easy interface for triggering notifications to multiple subscribers at once. This API is called **Topics** and allows users to manage their bulk notifications without having to implement complex loops. - -A topic is identified by a custom key and this key will be the identifier used when triggering notifications. You can assign a name to a topic for descriptive purposes. This name does not need to be unique and can be changed programmatically. - - - -The topic key should be unique and can't be changed once chosen. Novu also safeguards for key uniqueness behind the scenes. - - -A topic can have multiple subscribers who will receive a notification whenever a message is sent to the topic. - -## Create a Topic - -Copy and paste the following code into your app to create a topic: - -```php -// Create a Topic -$novu->createTopic([ - 'key' => 'frontend-users', - 'name' => 'All frontend users' -]); -``` - -Before running the code, make sure you understand the following: - -- When creating a `key`, ensure it is unique and accurately identifies the topic. Document naming conventions and communicate them to team members to avoid confusion and ensure a smooth workflow. -- The value of `name` should be a descriptive topic name. - -## Add subscribers to a Topic - -Copy and paste the following code into your app to add subscribers to a topic: - -```php -$topicKey = 'frontend-users'; -$subscribers = [ - '6460925ce1a93324257d2fc1', - '7789' -]; - -// Add subscribers to a topic -$novu->topic($topicKey)->addSubscribers($subscribers); -``` - -On the other hand, if you want to remove subscribers from a topic, do the following: - -```php -$topicKey = 'frontend-users'; -$subscribers = [ - '6460925ce1a93324257d2fc1', - '7789' -]; - -// Remove subscribers from a topic -$novu->topic($topicKey)->removeSubscribers($subscribers); -``` - -## Sending a notification to a Topic - -Thanks to the topics feature, it is possible to trigger a notification to all subscribers assigned to a topic. This helps avoid listing all subscriber identifiers in the `to` field of the notification trigger. - -To trigger a notification to all subscribers of a topic, copy and paste the code below: - -```php -// Send notifications to a topic (all frontend users) -$novu->triggerEvent([ - 'name' => 'first-email', - 'to' => [ - [ - 'type' => 'Topic', - 'topicKey' => 'frontend-users' - ] - ] -])->toArray(); -``` - -## Conclusion - -Great job! If you've reached this point, you should now have successfully created a subscriber, workflow, configured a channel provider and triggered a notification in your application. \ No newline at end of file diff --git a/quickstarts/python.mdx b/quickstarts/python.mdx deleted file mode 100644 index 9f1f0ae2..00000000 --- a/quickstarts/python.mdx +++ /dev/null @@ -1,280 +0,0 @@ ---- -title: 'Python quickstart' -description: 'Learn how to use Novu to quickly send multi-channel (SMS, Email, Chat, Push) notifications with Python.' -icon: 'python' ---- - -import SupportedChannelsSnippet from "/snippets/supported-channels.mdx"; - -In this guide, you'll learn how to use Novu in a Python application. Let's see how you can seamlessly integrate Novu into your Python project! - -## Prerequisites - -Before diving into the Quickstart, make sure you have the following: - -- Python3 installed on your development machine. -- A Novu account. If you don't have one, sign up for free at [web.novu.co](https://web.novu.co?utm_campaign=docs-gs-quick-python) - -## Install and Set Up Novu in your Python Project - -First, you must install the Novu package in your Python project. From your terminal, you can install the Novu package in your project by running either of the following two commands: - -Via pip - -```bash -pip install novu - -``` - -Via poetry - -```bash -poetry add novu - -``` - -Once installed, you can import Novu into your project and initialize it using your Novu account credentials. This step establishes a connection between your app and the Novu notification service. - -```python -from novu.config import NovuConfig - -NovuConfig().configure("https://api.novu.co", "") - -``` - -Replace the `` value with the authentic key from the **API Key** section of your [Novu Dashboard](https://web.novu.co/settings?utm_campaign=docs-quick-python). - -Please do not hardcode your credentials in a file in production. Use environment variables instead. - -## Set Up A Channel Provider - -A channel provider is a service that lets you use one or more notification channels such as sending an email, SMS, push notification etc. Our integration store currently supports four channels: Email, SMS, Chat, and Push. Each of these channels have multiple providers associated with them. - -| Channel | Providers | -| ------- | ------------------------------------------------------------------- | -| Email | MailGun, Mandrill, MailJet, Amazon SES, Sendgrid, Postmark, Netcore | -| SMS | Twilio, Amazon SNS, Plivo, SMS, SMSCentral, Kannel, Infobip, Termii | -| Chat | Mattermost, Slack, Microsoft Teams, Discord | -| Push | FCM, APNS, Expo | - -Only one provider can be **active** per **channel** at a time. Connect anyone of your preferred providers to get started. The email channel comes with Novu's email provider, which is active by default and includes 300 credits. - -## Create a Notification Workflow - -A notification workflow is the blueprint for the notifications that will be sent. It holds the entire flow of messages sent to the subscriber. - -The workflow includes the following: - -- Notification workflow name and Identifier -- Channel tailored content: - - - -Proper authorization needs to be set for the Chat channel for subscribers. - -To create a notification workflow, please follow the following steps: - -1. Click **Workflows** on the left sidebar of your Novu dashboard. -2. Click the **Add a Workflow** button on the top left. -3. Select **blank workflow** from the dropdown. -4. The name of a new workflow is currently "Untitled." Rename it to a suitable title. -5. Select any channel you want to use in your app. For the sake of this guide, we'll be using the 'Email' channel. - - Set email - - -6. Click on the recently added channel, fill the email subject and click “Update”. - - Update email template - - -7. Click on the “Test” tab and send a test email to verify your notification workflow. - - Send test email - -You should get an email within seconds. If you didn't, please check your 'spam' folder as sometimes test emails can end up there. Yaaay, you have successfully sent your first notification via the Novu dashboard! - -Now, let’s take it a step further to trigger notifications via code. - -## Create A Subscriber - -The recipients of a triggered notification are called subscribers. - -Click **Subscribers** on the left sidebar of the Novu dashboard to see all subscribers. By default, the dashboard will display only one subscriber, as you were added automatically during sign-up. If default subscriber is not created, checkout [how to create new subscriber.](/subscribers/subscribers#create-a-subscriber) - - - Subscriber id - - -Now, let's create a subscriber on Novu. Copy and paste the following code to do so: - -```python -## Create a subscriber - -from novu.api.subscriber import SubscriberApi -from novu.dto.subscriber import SubscriberDto - -your_subscriber_id = "123" # Replace this with a unique user ID. - -## Define a subscriber instance -subscriber = SubscriberDto( - subscriber_id=your_subscriber_id, - email="abc@gmail.com", - first_name="John", - last_name="Doe" -) - -SubscriberApi().create(subscriber) - -``` - -Run the code in your terminal like so: - -```bash -python main.py # replace main.py with your file name - -``` - -You should see a new subscriber (that you created above) on your Novu dashboard. - -## Update A Subscriber - -To update the Subscriber details you can call the put method from SubcriberApi. Here is an example: - -```python -## Update subscriber detail - -from novu.api.subscriber import SubscriberApi -from novu.dto.subscriber import SubscriberDto - -subscriber = SubscriberDto( - subscriber_id="123", - email="new.abc@gmail.com", - first_name="New", - last_name="Name" -) - -SubscriberApi().put(subscriber) - -``` - -To send notifications to all your users, you'll need to make them subscribers in Novu, which you can do by programmatically adding them to Novu. - -## Trigger A Notification - -Copy and paste the following code into your app to trigger a notification: - -```python -from novu.api import EventApi - -EventApi().trigger( - name="test", # The trigger ID of the workflow. It can be found on the workflow page. - recipients="123", - payload={}, # Your Novu payload goes here -) - -``` - -Before running the code, make sure you understand the following: - -- The value of `name` should be the notification workflow's trigger ID/slug. - - - Trigger id - - -- The value of `payload` is an array of the data that you want to be dynamically injected into the notification template content. -- The value of `recipients` is the id of the subscriber on Novu. Replace `123` with your subscriber ID. - -Run the code to trigger a notification! - -```bash -python main.py # replace main.py with your file name - -``` - -Next, we’ll learn how to send notifications to different groups of subscribers easily via **Topics.** - -## Topics - -Novu provides a simple API that offers an easy interface for triggering notifications to multiple subscribers at once. This API is called **Topics** and allows users to manage their bulk notifications without having to implement complex loops. - -A topic is identified by a custom key and this key will be the identifier used when triggering notifications. You can assign a name to a topic for descriptive purposes. This name does not need to be unique and can be changed programmatically. - -The topic key should be unique and can't be changed once chosen. Novu also safe guards for key uniqueness behind the scenes. - -A topic can have multiple subscribers who will receive a notification whenever a message is sent to the topic. - -## Create a Topic - -Copy and paste the following code into your app to create a topic: - -```python -from novu.api import TopicApi - -## Create a topic -TopicApi().create( - key="new-customers", name="New business customers" -) - -``` - -Before running the code, make sure you understand the following: - -- When creating a `key`, ensure it is unique and accurately identifies the topic. Document naming conventions and communicate them to team members to avoid confusion and ensure a smooth workflow. -- The value of `name` should be a descriptive topic name. - -## Add subscribers to a Topic - -Copy and paste the following code into your app to add subscribers a topic: - -```python -from novu.api import TopicApi - -## Add a list of subscribers to a topic -TopicApi().subscribe(key="new-customers", subscribers="") - -``` - -On the other hand, if you want to remove subscribers from a topic, do the following: - -```python -from novu.api import TopicApi - -## Unsubscribe a list of subscribers from a topic -TopicApi().unsubscribe(key="new-customers", subscribers="") - -``` - -## Sending a notification to a Topic - -Thanks to the topics feature, it is possible to trigger a notification to all subscribers assigned to a topic. This helps avoid listing all subscriber identifiers. - -To trigger a notification to all subscribers of a topic, copy and paste the code below: - -```python -from novu.api import EventApi -from novu.dto.topic import TriggerTopicDto - -topics = TriggerTopicDto( - topic_key="new-customers", - type="Topic", -) - -EventApi().trigger_topic( - name="test", # The trigger ID of the workflow. It can be found on the workflow page. - topics=topics, - payload={}, # Your Novu payload goes here -) - -``` - -## Next Steps - -Great job! If you've reached this point, you should now have successfully set up a channel provider, created a notification workflow, created a subscriber, updated a subscriber and triggered a notification in your application. - -To learn more about notifications and explore Novu's features and capabilities, check out: - -- [Novu Digest Engine](/workflows/digest) - Learn how to aggregate multiple trigger events into a single message and deliver it to the subscriber. -- [Novu Python SDK](https://github.com/novuhq/novu-python) - Delve deeper into the SDK and explore a lot of features. -- [Novu Python Cookbook](https://novu-python.readthedocs.io/en/latest/) - Learn how to use the SDK features. diff --git a/quickstarts/react.mdx b/quickstarts/react.mdx deleted file mode 100644 index 45b7f271..00000000 --- a/quickstarts/react.mdx +++ /dev/null @@ -1,193 +0,0 @@ ---- -title: "React" -description: "Learn how to use Novu to quickly send multi-channel (SMS, Email, Chat, Push) notifications with React." -icon: "react" ---- - -import SupportedChannelsSnippet from "/snippets/supported-channels.mdx"; - -Send notifications across different channels (SMS, Email, Chat, Push) and enable real-time In-App notifications with the help of a rich and customizable Notification Center. - -## Requirements - -To follow the steps in this quickstart, you'll need: - -- A Novu account. [Sign up for free](http://web.novu.co/?utm_campaign=docs-gs-quick-react) if you don’t have one yet. -- A working React development environment. - -You can find the code for this project [here](https://github.com/novuhq/react-quickstart) - -## Install the Novu React Notification Center Package in your React app - -The Novu React package provides a React component library that you can use to add a fully functioning notification center to your React application. - -To use it, first, install the React Notification Center package by running the following command in your terminal: - -```bash -npm install @novu/notification-center - -``` - -Before you can use Novu in your React app, you'll need two things: - -1. Create a workflow to use for sending notifications, and -2. Create a subscriber - recipient of notifications. - -## Create a workflow - -Before triggering a notification, we need to create a workflow. A workflow is like a blueprint that all the notifications are supposed to follow. - -> The recipients of a triggered notification are called subscribers. - -The workflow includes the following: - -- Workflow name and Identifier -- Channel-tailored content: - - - -These are the steps to create a workflow: - -1. Click **Workflow** on the left sidebar of your Novu dashboard. -2. Click the **Create Workflow** button on the top right. -3. The name of the new workflow is currently **"Untitled"**. Rename it to a more suitable title. -4. Select **In-App** as the channel you want to add. - - {" "} - -5. Click on the recently added "In-App" channel and configure it according to your preferences. Once you're done, click "Update" to save your configuration. - - {" "} - - I'll briefly explain the function of each label in the image above: - -- **1-Preview**: This shows you a glimpse of how each notification item will look in the Notification Center UI. -- **2-Avatar**: If turned on, each notification item will show the avatar of the subscriber. -- **3-Action**: With this, you can add a primary and secondary call to action button to each notification item. -- **4-Notification Feeds**: This displays a stream of specific notifications. You can have multiple feeds to show specific notifications in multiple tabs. -- **5-Redirect URL**: This is the URL to which a subscriber can be directed when they click on a notification item. -- **6-Filter**: This feature allows you to configure the criteria for delivering notifications. For instance, you can apply a filter based on a subscriber's online status to send them an email if they were online within the last hour. Read [more about filters](https://docs.novu.co/platform/step-filter/#subscriber-seen--read-filters). -- **7-Editor**: You can add text that you want to be displayed in each notification item. Additionally, you can specify custom variables using `{{ }}`. This means you can inject variables from your code into a notification item's text via a payload. - -In our case, we'll use the custom variables functionality, as shown below: - - - {" "} - - -Feel free to add only text for now and rename the workflow to quick start. It automatically creates a slug-like Identifier that will be needed in later steps to trigger a notification. - - - {" "} - - -Now, we’ll learn how to create subscribers on Novu - Recipients of Notifications! - -## Create a subscriber - -If you click “Subscriber” on the left sidebar of the [Novu dashboard](https://web.novu.co/subscribers?utm_campaign=docs-quick-react), you’ll see the subscriber list. By default, there will only be one subscriber as you’re automatically added as a subscriber when you sign up for Novu: - - - {" "} - - -If you don't see the default subscriber, follow [how to create subscriber](/subscribers/subscribers#create-a-subscriber) documentation to learn on how to create one - -Now, let’s create a subscriber on Novu. After creating a subscriber, we’ll trigger a notification to this subscriber. Subscribers are identified by a unique `subscriberId`. - -With Novu, you can create a subscriber using any of its SDKs (Node.js, PHP, .NET, Go, Ruby, Python and Kotlin). The NodeJS code to create a subscriber in Novu is: - -```jsx -import { Novu } from "@novu/node"; - -const novu = new Novu(""); - -await novu.subscribers.identify("789", { - firstName: "Sumit", - lastName: "Saurabh", -}); -``` - -The `identify` method used above either updates an existing subscriber using the payload data if a subscriber with the same `subscriberId` already exists, or creates a new one if it doesn't. Here, since no subscriber with `firstName` of `Sumit` and `lastName` of `Saurabh` exists, a new one was created and we can now send a notification to this subscriber by triggering a workflow. - - - You can read more about the `identify` method in our - [docs](https://docs.novu.co/subscribers/subscribers#create-a-subscriber) - - -You can get your API key from the Novu dashboard. Replace `` with it. Now, head over to the Novu dashboard and see the newly created subscriber above with a `subscriberId` of `789`. - -You can also update information about an already existing subscriber using the `subscriber.update` method as shown below: - -```jsx -import { Novu } from "@novu/node"; - -const novu = new Novu(""); - -await novu.subscribers.update("789", { - firstName: "Saurabh", // new first name - lastName: "Sumit", // new last name -}); -``` - -## Using Novu in a React app - -We have already installed the Novu notification center package above. To use it in an app, simply import it and add the component to your React app as follows: - -```jsx -import { - NovuProvider, - PopoverNotificationCenter, - NotificationBell, -} from "@novu/notification-center"; - -function App() { - return ( - <> - - - {({ unseenCount }) => } - - - - ); -} - -export default App; -``` - -You'll notice two things used above - `applicationIdentifier` and `subscriberID`. - -An application identifier is a public key used to identify your application. You can get your own application identifier from the [Novu dashboard settings](https://web.novu.co/settings?utm_campaign=docs-quick-react). - -And subscribers are users to whom notifications will be sent. They are identified by a `subscriberID` which you can also find in the [Novu subscribers dashboard](https://web.novu.co/subscribers?utm_campaign=docs-quick-react). - -## Trigger a notification - -We can trigger a notification by simply running the code below with the correct credentials; - -```jsx -import { Novu } from "@novu/node"; - -const novu = new Novu(""); - -novu.trigger("quickstart", { - to: { - subscriberId: "789", - }, - payload: { - description: "Test notification", - }, -}); -``` - -This will take the workflow we created above with the identifier `quickstart` and send a notification to the subscriber with `subscriberId` of `789`. - -> Make sure you're executing this code with the correct credentials. - -## Conclusion - -Great job! If you've reached this point, you should now have successfully set up the notification centre, created a subscriber, a workflow, and triggered a notification in your React application. diff --git a/quickstarts/redwoodjs.mdx b/quickstarts/redwoodjs.mdx deleted file mode 100644 index c8e9e258..00000000 --- a/quickstarts/redwoodjs.mdx +++ /dev/null @@ -1,312 +0,0 @@ ---- -title: "RedwoodJS" -description: "Learn how to use Novu to quickly send multi-channel (SMS, Email, Chat, Push) notifications with RedwoodJS." -icon: "js" ---- - -import SupportedChannelsSnippet from "/snippets/supported-channels.mdx"; - -Send notifications across different channels (SMS, Email, Chat, Push) and enable real-time In-App notifications with the help of a rich and customizable Notification Center in a RedwoodJS app. - -Explore the code for [this quickstart on GitHub](https://github.com/novuhq/redwoodjs-quickstart/). - -Explore the [live demo](https://novu-redwoodjs-blog.netlify.app/contact). - -# Prerequisites - -To follow the steps in this quickstart, you'll need: - -- A Novu account. If you don't have one, [sign up for free](https://web.novu.co?utm_campaign=docs-gs-quick-redwoodjs). -- Node.js installed on your development machine. - -# Create a RedwoodJS app - -Clone the popular RedwoodJS blog tutorial app by running the following command in your terminal: - -```bash -git clone https://github.com/redwoodjs/redwood-tutorial -cd redwood-tutorial -yarn install -yarn rw prisma migrate dev -yarn rw prisma db seed -yarn rw dev - -``` - -# Install Novu - -To install Novu's In-App notification center, simply issue the following command: - -```bash -yarn workspace web add @novu/notification-center - -``` - -Next, install Novu's Node SDK to fire notifications from the api/backend: - -```bash -yarn workspace api add @novu/node - -``` - -## Register The Notification Center Component - -Head over to `web/src/layouts/BlogLayout/BlogLayout.js` file of the recently cloned project and add the Novu In-App Notification center. - -The file should look like this: - -```javascript -import { - NovuProvider, - PopoverNotificationCenter, - NotificationBell, -} from "@novu/notification-center"; - -import { Link, routes } from "@redwoodjs/router"; - -import { useAuth } from "src/auth"; - -const BlogLayout = ({ children }) => { - const { logOut, isAuthenticated, currentUser } = useAuth(); - const SUBSCRIBER_ID = process.env.REDWOOD_ENV_SUBSCRIBER_ID; - const APPLICATION_IDENTIFIER = process.env.REDWOOD_ENV_APPLICATION_IDENTIFIER; - - return ( - <> -
    -

    - - Redwood Blog with Novu - -

    - -
    -
    - {children} -
    - - ); -}; - -export default BlogLayout; -``` - - - Add `REDWOOD_ENV_APPLICATION_IDENTIFIER` and `REDWOOD_ENV_SUBSCRIBER_ID` to - the `.env` file.{" "} - - -The `Subscriber ID` ideally should be the `id` of the **logged in user**. When a user logs into your app, they’ll be able to see all their notifications in the notification center. For this to work successfully, the user should have been created as a subscriber on Novu. - -The `Application Identifier` is a public key used to identify your application. - -Head over to your [Novu dashboard settings](https://web.novu.co/settings?utm_campaign=docs-quick-redwoodjs), copy the application identifier and add it as the value of `REDWOOD_ENV_APPLICATION_IDENTIFIER`. - -Head over to [the Subscribers page](https://web.novu.co/subscribers?utm_campaign=docs-quick-redwoodjs), grab a subscriber identifier and add it as the value of `REDWOOD_ENV_SUBSCRIBER_ID` in the `.env` file. - -Now, check your app, you will see a notification bell button. Click on the button to reveal the notification center UI. Voila! - - - There are no notifications because none has been triggered yet. When - notifications are sent to a subscriber, it will show up in the UI. Next, we'll - learn how to trigger notifications. - - -# Create A Workflow - -The first step to trigger notifications is to create a workflow. A workflow is like a map that holds the entire flow of messages sent to the subscriber. - -The recipients of a triggered notification are called subscribers. - -The workflow includes the following: - -- Workflow name and Identifier -- Channel tailored content: - - - -Please proceed to create a workflow. - -1. Click **Workflows** on the left sidebar of your Novu dashboard. -2. Click the **Add a workflow** button on the top left. -3. The name of the new workflow is currently "Untitled." Rename it to a more suitable title. -4. Select **In-App** as the channel you want to add. -5. Click on the recently added In-App channel and configure it. - - - {" "} - -I’ll briefly explain the function of each label in the image above. - -- **1-Preview**: Shows you a glimpse of how each notification item will look like in the Notification Center UI. -- **2-Avatar:** If turned on, each notification item will show the avatar of the subscriber. -- **3-Action:** With this, you can add a primary and secondary call to action button to each notification item. -- **4-Notification Feeds:** This displays a stream of specific notifications. You can have multiple feeds to show specific notifications in multiple tabs. -- **5-Redirect URL** - This is the URL to which a subscriber can be directed when they click on a notification item. -- **6-Filter** - This feature allows you to configure the criteria for delivering notifications. For instance, you can apply a filter based on a subscriber's online status to send them an email if they were online within the last hour. Read [more about filters](https://docs.novu.co/platform/step-filter/#subscriber-seen--read-filters). - -In the In-App channel `Editor`, add the following content and rename the step to `Onboarding In App` : - -```javascript -Hi {{ name }}, -
    -

    Thanks for indicating interest in attending RedwoodJS conference. Here is your message:

    - -

    {{ message }}

    - -``` - - - {" "} - `{{ name }}` and `{{ message }}` are payload values that will be passed on from - our code to the workflow template so it can appear in the notification sent to - subscribers. - - -Click on the `Update` button to save the configuration successfully! - -6. In the workflow page, drag and drop the **Email** channel to the workflow canvas to be just below the `In-App` channel. -7. Click on the recently added Email channel and configure it: - -Add `Sender name` and `Subject`. Ensure the `Default Layout` is selected for the Email Layout. - -Switch to the `Custom Code` section and add the following: - -```javascript -Hi {{ name }}, - -
    -

    Thank you for attending RedwoodJS Conference. This means a lot to us!

    -
    -

    Here is your message:

    - -{{ message }} - -``` - -Finally, give the workflow a name. We'll need the workflow trigger identifier **_(which is the slug of the name)_** later. - -Next, we’ll learn how to trigger notifications to subscribers. - -# Trigger A Notification - -Open up `api/src/services/contacts/contacts.js`, and add the following code into your app to trigger a notification: - -```javascript -import { Novu } from '@novu/node' - -const novu = new Novu(process.env.NOVU_API_KEY) - -export async function sendNotification(email, name, message) { - await novu.trigger('', { - to: { - subscriberId: process.env.REDWOOD_ENV_SUBSCRIBER_ID, - email: email, - }, - payload: { - name: name, - message: message, - }, - }) -} - -... - -... - -/** In the createContact code, call the sendNotification and pass in the email, name and message */ -export const createContact = ({ input }) => { - validate(input.email, 'email', { email: true }) - - sendNotification(input.email, input.name, input.message) - - return db.contact.create({ - data: input, - }) -} - -... -... - -``` - -Before running the code, make sure you understand the following: - -- The value of `` should be the workflow’s trigger ID/slug. -- The value of `payload` is an array of the data that you want to be dynamically injected into the workflow content. -- The value of `subscriberId` is the id of the subscriber on Novu. -- Ensure `NOVU_API_KEY` is in the `.env` file and with the appropriate value gotten from the dashboard. - -# One more thing - -Head over to the [integrations store on your Novu dashboard](https://web.novu.co/integrations?utm_campaign=docs-quick-redwoodjs). - -Select the **Novu Email Test Provider**, and mark as the primary provider for Emails. This ensures that all emails are sent via the Novu Email provider. - -# Test the app - -Ensure your app is still running and head over the `/contact` page in the app. Fill the form and click `Save`. - -The app will trigger two notifications: - -- An In-App notification -- An Email notification - -# Conclusion - -Great job! If you've reached this point, you should now have successfully created workflow, configured a channel provider, triggered a workflow, and sent an email and In-App notification. diff --git a/quickstarts/ruby.mdx b/quickstarts/ruby.mdx deleted file mode 100644 index 1a2d075d..00000000 --- a/quickstarts/ruby.mdx +++ /dev/null @@ -1,327 +0,0 @@ ---- -title: 'Ruby quickstart' -description: 'Learn how to use Novu to quickly send multi-channel (SMS, Email, Chat, Push) notifications with Ruby.' -icon: 'gem' ---- - -import SupportedChannelsSnippet from "/snippets/supported-channels.mdx"; - -Learn how to integrate Novu into your Ruby project on the fly and send notifications across different channels (SMS, Email, Chat, Push). - -Let's get started! 💪 - -## Requirements - -To follow the steps in this quickstart, you'll need: - -- A Novu account. [Sign up for free](http://web.novu.co/?utm_campaign=docs-gs-quick-ruby) if you don’t have one yet. -- A working Ruby development environment with at least version 2.6.0. - -You can also [view the completed code](https://github.com/novuhq/novu-ruby-quickstart) of this quick start in a GitHub repo. - -## Install Novu Ruby SDK - -The [Ruby SDK](http://github.com/novuhq/novu-ruby) provides a fluent and expressive interface for interacting with Novu's API and managing notifications. - -You can install the client library via RubyGems: - -```ruby -gem install novu - -``` - -Or add it to your Gemfile: - -```ruby -gem 'novu' - -``` - -Then run `bundle install`. - -## Initialize & Configure the Novu Ruby SDK - -To use the library, Create a new file `index.rb` then initialize the client with your API token: - -```ruby -require 'novu' - -client = Novu::Client.new('') - -``` - -Replace the `` argument with the authentic key from the **API Key** section of your [Novu Dashboard](https://web.novu.co/settings?utm_campaign=docs-quick-ruby). - - - -Please do not hardcode your credentials in a file in production. Use environment variables instead. - - -To test your API key, call the methods on the client to interact with the Novu API and display the result on your ruby console: - -```ruby -puts client.notifications - -``` - -Run the file `index.rb`: - -```bash -ruby index.rb - -``` - -You should see a result as below: - -```json -{ - "page": 0, - "totalCount": 0, - "pageSize": 10, - "data": [] -} - -``` - -## Set Up A Channel Provider - -A channel provider is a service that provides one or more notification functionality such as sending an email, SMS, push notification etc. Our [integration store](https://web.novu.co/integrations?utm_campaign=docs-quick-ruby) includes four channels: Email, SMS, Chat, and Push. These channels have multiple providers associated with them. - -| Channel | Providers | -| --- | --- | -| Email | MailGun, Mandrill, MailJet, Amazon SES, Sendgrid, Postmark, Netcore | -| SMS | Twilio, Amazon SNS, Plivo, SMS, SMSCentral, Kannel, Infobip, Termii | -| Chat | Mattermost, Slack, Microsoft Teams, Discord | -| Push | FCM, APNS, Expo | - -Only one provider can be **active** per **channel**. Connect any of your favorite providers to get started. The email channel comes with Novu's email provider, which is active by default and includes 300 credits. - -## Create A Workflow - -A workflow is a blueprint for the notifications that will be sent. It holds the entire flow of messages sent to the subscriber. This is where all the different channels are tied together under a single entity. - -The workflow includes the following: - -- Workflow name and Identifier -- Channel-tailored content: - - - - -Proper authorization needs to be set for the Chat channel for subscribers. - - -Please proceed to create a workflow. - -1. Click **Workflows** on the left sidebar of your Novu dashboard. -2. Click the **Create Workflow** button on the top right. - -3. The name of the new workflow is currently Untitled, rename it to a more suitable title. - -4. Select Email as the channel you want to add, by dragging it from the right sidebar. - -5. Click on the Email in the workflow and edit it as per this image. Don’t forget to add the fields in the editor which is supposed to be updated with dynamic values that will be sent when calling the API. - -6. Also, add the variables in the Variables section in the test tab and try testing it by sending the email to your email address using the Send Test Email button at the bottom right. - -You should get an email within seconds! 👌 - -Great, you have successfully sent your first notification via the Novu dashboard! Now, let’s take a step further to trigger notifications via code. - -## Create A Subscriber - -The recipients of a triggered notification are called subscribers. - -Click **Subscribers** on the left sidebar of the Novu dashboard to see all subscribers. By default, the dashboard will display a subscriber, as you were added automatically during sign-up. - -Now, let's create a subscriber on Novu. Copy and paste the following code to do so: -```ruby -require 'novu' - -client = Novu::Client.new('') - -## Create subscriber - -create_payload = { - 'subscriberId' => '7789', # replace with system_internal_user_id - 'firstName' => 'John', # optional - 'lastName' => 'Doe', # optional -} -client.create_subscriber(create_payload) - -``` - -Run the code in your terminal: - -```bash -ruby index.rb - -``` - -You should see the subscriber on your Novu dashboard. - -!https://res.cloudinary.com/dxc6bnman/image/upload/f_auto,q_auto/v1685466979/guides/Screenshot_2023-05-14_at_11.06.38_ugvmc0.png - -I’d like to publicly announce that `abc@gmail.com` is a random unlikely email your users will have. To update this to an alternative email, you can call the `updateSubscriber` method like so: - -```ruby -require 'novu' - -client = Novu::Client.new('') - -## Update subscriber detail - -update_payload = { - 'email' => 'validemail@gmail.com', # replace with valid email - 'firstName' => '', # optional - 'lastName' => 'Obasanjo', # optional -} -client.update_subscriber('7789', update_payload); -``` - -Other valid fields that can be updated are `phone`, `avatar`, and `data`. The `data` field can accept an object of metadata that you want to attach to the subscriber. - - - -To make all of your app users subscribers, you need to programmatically add them to Novu. - - -## Trigger A Notification - -Copy and paste the following code into your app to trigger a notification: - -```ruby -require 'novu' - -client = Novu::Client.new('') - -## trigger a notification - -trigger_payload = { - 'name' => 'Trigger1', - 'payload' => { - 'first-name' => 'Adam' - }, - 'to' => { - 'subscriberId' => '7789' - } -} -client.trigger_event(trigger_payload) - -``` - -Before running the code, make sure you understand the following: - -- The value of `name` should be the workflow’s trigger ID/slug. - - -- The value of `payload` is an array of the data that you want to be dynamically injected into the workflow content. -- The value of `subscriberId` is the id of the subscriber on Novu. Replace `7789` with your subscriber ID. - -Run the code to trigger a notification! - -```bash -ruby index.rb - -``` - -Next, we’ll learn how to send notifications to different groups of subscribers easily via **Topics.** - -## Topics - -Novu provides a simple API that offers an easy interface for triggering notifications to multiple subscribers at once. This API is called **Topics** and allows users to manage their bulk notifications without having to implement complex loops. - -A topic is identified by a custom key and this key will be the identifier used when triggering notifications. You can assign a name to a topic for descriptive purposes. This name does not need to be unique and can be changed programmatically. - - - -The topic key should be unique and can't be changed once chosen. Novu also safeguards for key uniqueness behind the scenes. - - -A topic can have multiple subscribers who will receive a notification whenever a message is sent to the topic. - -## Create a Topic - -Copy and paste the following code into your app to create a topic: - -```ruby -require 'novu' - -client = Novu::Client.new('') -## Create a Topic - -create_topic_payload = { - 'key' => 'frontend-users', - 'name' => 'All frontend users' -} -client.create_topic(create_topic_payload) - -``` - -Before running the code, make sure you understand the following: - -- When creating a `key`, ensure it is unique and accurately identifies the topic. Document naming conventions and communicate them to team members to avoid confusion and ensure a smooth workflow. -- The value of `name` should be a descriptive topic name. - -## Add and Remove Subscribers to a Topic - -Copy and paste the following code into your app to add subscribers to a topic: - -```ruby -require 'novu' - -client = Novu::Client.new('') - -topicKey = 'frontend-users' -subscribers = ['6460925ce1a93324257d2fc1', '7789'].to_s - -## Add subscribers to a topic -client.add_subscribers(topicKey, subscribers) - -``` - -Also, you can remove subscribers from a topic by using the code snippet below: - -```ruby -require 'novu' - -client = Novu::Client.new('') - -topicKey = 'frontend-users' -subscribers = ['6460925ce1a93324257d2fc1', '7789'].to_s - -## Remove subscribers from a topic -client.remove_subscribers(topicKey, subscribers) - -``` - -## Sending a notification to a Topic - -Thanks to the topics feature, it is possible to trigger a notification to all subscribers assigned to a topic. This helps avoid listing all subscriber identifiers in the `to` field of the notification trigger. - -To trigger a notification to all subscribers of a topic, copy and paste the code below: - -```ruby -require 'novu' - -client = Novu::Client.new('') - -## Send notifications to a topic (all frontend users) -client.trigger_event({ - 'name' => 'Trigger1', - 'to' => { - 'type' => 'Topic', - 'topicKey' => 'frontend-users' - } -}) - -``` - - - -The value of the `name` key in the payload should be the name of the notification created earlier. - - -## Conclusion - -Great job! If you've reached this point, you should now have successfully created a subscriber, workflow, configured a channel provider and triggered a notification in your application. diff --git a/quickstarts/vanillajs.mdx b/quickstarts/vanillajs.mdx deleted file mode 100644 index e6da68f9..00000000 --- a/quickstarts/vanillajs.mdx +++ /dev/null @@ -1,266 +0,0 @@ ---- -title: 'VanillaJS' -description: 'Learn how to use Novu to quickly send multi-channel (SMS, Email, Chat, Push) notifications with VanillaJS.' -icon: 'js' ---- - -import SupportedChannelsSnippet from "/snippets/supported-channels.mdx"; - -Learn how to integrate Novu into your vanilla JS app on the fly. Send notifications across different channels (SMS, Email, Chat, Push) and enable real-time In-App notifications with the help of a rich and customizable Notification Center. - -## Requirements - -To follow the steps in this quickstart, you'll need: - -- A Novu account. [Sign up for free](http://web.novu.co/?utm_campaign=docs-gs-quick-vanilla) if you don’t have one yet. -- A working Vanilla JS development environment. - -You can find the code for this project [here](https://github.com/novuhq/vanillajs-quickstart) - -## Loading Notification center component from the CDN - -The Notification Center Web Component is a custom element that can be used in any web application. - -You can find out more about the web component [here](https://docs.novu.co/notification-center/web-component/). - -In our case, we'll be using the bundled version of the Notification Center Web Component that is available on the CDN. - -You can load the Notification center component using the CDN as follows: - -```html - - - - ... - - - - - - - - - - -``` - -You'll notice the use of two things in the above code - `application-identifier` and `subscriber-id`. - -An application identifier is a public key used to identify your application. You can get your own application identifier from the [Novu dashboard settings](https://web.novu.co/settings?utm_campaign=docs-quick-vanilla). - -And subscribers are users to which notifications will be sent. They are identified by a `subscriberID` which you can also find in the [Novu subscribers dashboard](https://web.novu.co/subscribers?utm_campaign=docs-quick-vanilla). Let's learn more about subscribers. - -## Subscribers in Novu - -If you click “Subscriber” on the left sidebar of the [Novu dashboard](https://web.novu.co/subscribers?utm_campaign=docs-quick-vanilla), you’ll see the subscriber list. By default, there will only be one subscriber as you’re automatically added as a subscriber when you sign up for Novu: - - -If you don't see the default subscriber, follow [how to create subscriber](/subscribers/subscribers#create-a-subscriber) documentation to learn on how to create one - -Now, let’s create a subscriber on Novu. After creating a subscriber, we’ll trigger a notification to this subscriber. Subscribers are identified by a unique `subscriberId`. - -With Novu, you can create a subscriber using any of its SDKs (Node.js, PHP, .NET, Go, Ruby, Python and Kotlin). The code to create a subscriber in Novu is: - -```typescript -import { Novu } from '@novu/node'; - -const novu = new Novu(''); - -await novu.subscribers.identify('123', { - firstName: 'Sumit', - lastName: 'Saurabh', - returnUser: true, -}); -``` - -You can get your API key from the Novu dashboard. Replace `` with it. Now, if you’ll go to the Novu dashboard, you shall see the subscriber we created above with `subscriberId` of `123`. - - -You can also update information about an already existing subscriber using the `subscriber.update` method as shown below: - -```typescript -import { Novu } from '@novu/node'; - -const novu = new Novu(''); - -await novu.subscribers.update('123', { - firstName: 'Saurabh', // new first name - lastName: 'Sumit', // new last name -}); -``` - -## Create a workflow - -Before triggering a notification, we need to create a workflow. A workflow is like a blueprint that all the notifications are supposed to follow. - -> The recipients of a triggered notification are called subscribers. -> - -The workflow includes the following: - -- Workflow name and Identifier -- Channel tailored content: - - - -These are the steps to create a workflow: - -1. Click **Workflows** on the left sidebar of your Novu dashboard. -2. Click the **"Create Workflow"** button on the top right. -3. The name of the new workflow is currently **"Untitled"**. Rename it to a more suitable title. -4. Select **"In-App"** as the channel you want to add. - -5. Click on the recently added In-App channel and configure it according to your preferences. Once you’re done, click Update to save your configuration. - -I’ll briefly explain the function of each label in the image above. -- **1-Preview**: Shows you a glimpse of how each notification item will look like in the Notification Center UI. -- **2-Avatar:** If turned on, each notification item will show the avatar of the subscriber. -- **3-Action:** With this, you can add a primary and secondary call to action button to each notification item. -- **4-Notification Feeds:** This displays a stream of specific notifications. You can have multiple feeds to show specific notifications in multiple tabs. -- **5-Redirect URL** - This is the URL to which a subscriber can be directed when they click on a notification item. -- **6-Filter** - This feature allows you to configure the criteria for delivering notifications. For instance, you can apply a filter based on a subscriber's online status to send them an email if they were online within the last hour. - -In our case, we'll use the custom variables functionality, as shown below: - -Feel free to add only text for now and rename the workflow to quickstart. It automatically creates a slug-like Identifier that will be needed in later steps to trigger a notification. - -Having created a workflow and having our `subscriberID`, we're ready to send notifications using Novu! - -## Using Novu in a vanilla JS app - -We have already used the CDN to get the notification center component and we already have all the credentials that we need. To use it in an app, simply use the `notification-center-component` in your html file as shown below. - -```html - - - - ... - - - - - - - - - -``` - -You can add all the logic you want to add to the `script` element at the bottom or move it all into a separate JavaScript file (don't forger to link to the JavaScript file in this case). - -Here's an example of such an HTML file: - -```html - - - - - - - - Vanilla JS app - - - -
    -

    Notification generator

    - -
    -
    -
    - - -
    -
    - - - -``` - -And the corresponding script file is as follows: - -```jsx -const btn =document.querySelector('.btn'); -const input =document.querySelector('.input'); -const form =document.querySelector('.form'); - -const onClickHandler = async (e) => { - e.preventDefault(); - const desc = input.value; - console.log(desc); - try { - const resp = await fetch('', { - method: 'POST', - body: JSON.stringify({ - description: desc, - }), - headers: { - 'Content-Type': 'application/json', - }, - }); - input.value = ''; - console.log(resp); - } catch (err) { - console.log(err); - } -}; - -form.addEventListener('submit', onClickHandler); - -// here you can attach any callbacks, interact with the web component API -let nc =document.getElementsByTagName('notification-center-component')[0]; -nc.onLoad = () => console.log('hello world!'); -``` - -The front-end as well as the back-end code for the sample application can be found [here](https://github.com/novuhq/vanillajs-quickstart) for illustration. - -## Code to trigger the notification - -We can trigger a notification by simply executing the trigger code snippet we get from the Novu web dashboard. - -To get the snippet: - -1. Go to **Workflows** in the left sidebar on Novu web dashboard. -2. Select the workflow. -3. Click the **Get Snippet** button on the top right. - -Here's the trigger snippet you'll receive: - -```jsx -import { Novu } from '@novu/node'; - -const novu = new Novu(''); - -novu.trigger('quickstart', { - to: { - subscriberId: '', - }, - payload: { - description: '', - }, -}); -``` - -> Make sure you're executing this code with the correct credentials. -> - -## Conclusion - -Great job! If you've reached this point, you should now have successfully set up the notification center, created a subscriber, a workflow, configured a channel provider and triggered a notification in your React application. diff --git a/quickstarts/vue.mdx b/quickstarts/vue.mdx deleted file mode 100644 index 6c48464e..00000000 --- a/quickstarts/vue.mdx +++ /dev/null @@ -1,296 +0,0 @@ ---- -title: 'Vue' -description: 'Learn how to use Novu to quickly send multi-channel (SMS, Email, Chat, Push) notifications with Vue.' -icon: 'vuejs' ---- - -import SupportedChannelsSnippet from "/snippets/supported-channels.mdx"; - -Learn how to use Novu to quickly send multi-channel (SMS, Email, Chat, Push) notifications and integrate a rich, customizable and ready-to-use realtime UI In-App notification center in Vue apps. - -# Requirements - -To follow the steps in this quickstart, you'll need: - -- A Novu account. [Sign up for free](http://web.novu.co/?utm_campaign=docs-gs-quick-vue) if you don’t have one yet. -- A working Vue development environment with a Vue 3 version. - -You can also [view the completed code](https://github.com/novuhq/vue-quickstart) of this quick start in a GitHub repo. - -# Create a new Vue app - -If you already have a working Vue app, please feel free to skip this section. - -Create a new Vue 3 app by running the following command in your terminal: - -```bash -npx create-vue notifications-app -``` - -Interactively run through the questions and answer them according to your preference. Then `cd` into the `notifications-app` folder and run the following: - -```bash -npm install && npm run format && npm run dev -``` - -# Install Novu Vue Notification Center Package - -The Novu Vue package provides a Vue component wrapper over the web component that you can use to integrate the notification center into your Vue application. - -Now install the Vue Notification Center package by running the following command in your terminal: - -```bash -npm install @novu/notification-center-vue -``` - -## Register The Notification Center Plugin - -First, register the notification center plugin in your app as a global component. - -In your `main.js` or `main.ts` file, add it like so: - -```tsx -import { createApp } from 'vue'; - -import NotificationCenterPlugin from '@novu/notification-center-vue'; -import '@novu/notification-center-vue/dist/style.css'; - -// your app component -import App from './App.vue'; - -const app = createApp(App); - -app.use(NotificationCenterPlugin); -app.mount('#app'); -``` - -# Use The Notification Center Component - -Create a new file, `NotificationCenter.vue` in the `src/components` directory. This file will house the Notification Center component. - -Add the `NotificationCenterComponent` like so: - -***Using the Composition API*** - -```tsx - - - -``` - -src/components/NotificationCenter.vue - -***Using the Options API*** - -```tsx - - - -``` - -src/components/NotificationCenter.vue - -The `subscriberId` ideally should be the `id` of the **logged in user**. When a user logs into your app, they’ll be able to see all their notifications in the notification center. For this to work successfully, the user should have been created as a subscriber on Novu. - -The `applicationIdentifier` is a public key used to identify your application. Obtain it from your [Novu dashboard settings](https://web.novu.co/settings?utm_campaign=docs-quick-vue), and add it to your `.env` file. - -If there’s none, create a `.env` file and add it like so: - -``` -VITE_NOVU_APP_IDENTIFIER = YOUR_NOVU_APP_IDENTIFIER; -``` - -Replace `YOUR_NOVU_APP_IDENTIFIER` with the real value from your dashboard. - -# Display The In-App Notification Center UI - -If you started by creating a new Vue app, then do the following: - -Head over to `src/views/HomeView.vue` and import the `NotificationCenter.vue` component. - -Replace the entire file with the code below: - -```tsx - - - -``` - -src/views/HomeView.vue - -Run your app again. You will see a button with the text **Notifications: 0**. Click on the button to reveal the notification center UI. Voila! - - - -There are no notifications because none has been triggered yet. When notifications are sent to a subscriber, it will show up in the UI. Next, we'll learn how to trigger notifications. - - -There’s a probability you might want the general notification bell icon. By default, the component displays the bell icon. - -Simply delete the button element. Your component should look like this: - -```jsx - -``` - -You should now see a **bell button** that opens the notification center when clicked. This bell can be customized to your preference, and we will style and modify it in a later step. - -- [Vue Notification Center Example Repo](https://github.com/novuhq/examples/tree/main/vue-notification-center-example) - -# Create A Workflow - -The first step to trigger notifications is to create a workflow. A workflow is like a map that holds the entire flow of messages sent to the subscriber. - - - -The recipients of a triggered notification are called subscribers. - - -The workflow includes the following: - -- Workflow name and Identifier -- Channel tailored content: - - - -Please proceed to create a workflow. - -1. Click **Workflows** on the left sidebar of your Novu dashboard. -2. Click the **Create Workflow** button on the top right. -3. The name of the new workflow is currently "Untitled." Rename it to a more suitable title. -4. Select **In-App** as the channel you want to add. - -5. Click on the recently added In-App channel and configure it according to your preferences. Once you’re done, click Update to save your configuration. - -I’ll briefly explain the function of each label in the image above. - -- **1-Preview**: Shows you a glimpse of how each notification item will look like in the Notification Center UI. -- **2-Avatar:** If turned on, each notification item will show the avatar of the subscriber. -- **3-Action:** With this, you can add a primary and secondary call to action button to each notification item. -- **4-Notification Feeds:** This displays a stream of specific notifications. You can have multiple feeds to show specific notifications in multiple tabs. -- **5-Redirect URL** - This is the URL to which a subscriber can be directed when they click on a notification item. -- **6-Filter** - This feature allows you to configure the criteria for delivering notifications. For instance, you can apply a filter based on a subscriber's online status to send them an email if they were online within the last hour. Read [more about filters](https://docs.novu.co/platform/step-filter/#subscriber-seen--read-filters). - -Feel free to add only text for now and rename the workflow to `Onboarding In App`. It automatically creates a slug-like Identifier that will be needed in later steps to trigger a notification. - - - -Next, we’ll learn how to create subscribers on Novu - *Recipients of Notifications* - -# Create A Subscriber - -Click **Subscribers** on the left sidebar of the [Novu dashboard](https://web.novu.co/subscribers?utm_campaign=docs-quick-vue) to see all subscribers. By default, the dashboard will display a subscriber, as you were added automatically during sign-up. - - -Now, let's create a subscriber on Novu. - -Novu has a plethora of backend SDKs (Node.js, PHP, .NET, Go, Ruby, Python and Kotlin) to choose from to create a subscriber programmatically. This is the recommended method. - -Obtain your API key from your [Novu dashboard.](https://web.novu.co/settings?utm_campaign=docs-quick-vue) Replace `` with it. - -Now check your Novu dashboard. You should see the recently created subscriber. - -You can also update the subscriber info like so: - -```jsx -import { Novu } from '@novu/node'; - -const novu = new Novu(''); - -await novu.subscribers.update('132', { - email: 'janedoe@domain.com', // new email - phone: '+19874567832', // new phone -}); -``` - -# Trigger A Notification - -To trigger a notification, simply run the codesandbox below with the correct credentials. - -```jsx -// code to trigger a notification -``` - -`onboarding-in-app` is the workflow identifier we created earlier. - -Ensure the `subscriberId` value in the backend code that triggers the notification matches the `subscriberId` in your **NotificationCenterComponent** code. - -```tsx - - - -``` - -Check your app again. You should see the recently triggered notification! - -# Conclusion - -Great job! If you've reached this point, you should now have successfully set up the notification center, created a subscriber, a workflow, configured a channel provider and triggered a notification in your Vue application. - diff --git a/framework/recipes/json-schema.mdx b/recipes/json-schema.mdx similarity index 87% rename from framework/recipes/json-schema.mdx rename to recipes/json-schema.mdx index 42254914..794ffdbc 100644 --- a/framework/recipes/json-schema.mdx +++ b/recipes/json-schema.mdx @@ -15,10 +15,7 @@ Learn more about JSON Schema at [json-schema.org](https://json-schema.org/). ```json { "type": "object", - "required": [ - "firstName", - "lastName" - ], + "required": ["firstName", "lastName"], "properties": { "firstName": { "type": "string", @@ -42,9 +39,7 @@ Learn more about JSON Schema at [json-schema.org](https://json-schema.org/). ```json { "type": "object", - "required": [ - "title" - ], + "required": ["title"], "properties": { "title": { "type": "string", @@ -55,9 +50,7 @@ Learn more about JSON Schema at [json-schema.org](https://json-schema.org/). "title": "Tasks", "items": { "type": "object", - "required": [ - "title" - ], + "required": ["title"], "properties": { "title": { "type": "string", @@ -99,11 +92,7 @@ Learn more about JSON Schema at [json-schema.org](https://json-schema.org/). "type": "string" } }, - "required": [ - "street_address", - "city", - "state" - ] + "required": ["street_address", "city", "state"] }, "node": { "type": "object", @@ -211,9 +200,7 @@ Learn more about JSON Schema at [json-schema.org](https://json-schema.org/). "type": "string" } }, - "required": [ - "lorem" - ] + "required": ["lorem"] }, { "properties": { @@ -221,9 +208,7 @@ Learn more about JSON Schema at [json-schema.org](https://json-schema.org/). "type": "string" } }, - "required": [ - "ipsum" - ] + "required": ["ipsum"] } ] } @@ -236,10 +221,7 @@ Learn more about JSON Schema at [json-schema.org](https://json-schema.org/). "type": "object", "properties": { "animal": { - "enum": [ - "Cat", - "Fish" - ] + "enum": ["Cat", "Fish"] } }, "allOf": [ @@ -255,16 +237,10 @@ Learn more about JSON Schema at [json-schema.org](https://json-schema.org/). "properties": { "food": { "type": "string", - "enum": [ - "meat", - "grass", - "fish" - ] + "enum": ["meat", "grass", "fish"] } }, - "required": [ - "food" - ] + "required": ["food"] } }, { @@ -279,29 +255,18 @@ Learn more about JSON Schema at [json-schema.org](https://json-schema.org/). "properties": { "food": { "type": "string", - "enum": [ - "insect", - "worms" - ] + "enum": ["insect", "worms"] }, "water": { "type": "string", - "enum": [ - "lake", - "sea" - ] + "enum": ["lake", "sea"] } }, - "required": [ - "food", - "water" - ] + "required": ["food", "water"] } }, { - "required": [ - "animal" - ] + "required": ["animal"] } ] } @@ -313,11 +278,7 @@ Learn more about JSON Schema at [json-schema.org](https://json-schema.org/). { "definitions": { "locations": { - "enumNames": [ - "New York", - "Amsterdam", - "Hong Kong" - ], + "enumNames": ["New York", "Amsterdam", "Hong Kong"], "enum": [ { "name": "New York", diff --git a/resources/glossary.mdx b/resources/glossary.mdx deleted file mode 100644 index 8bc30d2f..00000000 --- a/resources/glossary.mdx +++ /dev/null @@ -1,22 +0,0 @@ ---- -title: "Glossary" -description: "Example overview page before API endpoints" ---- - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas et eros iaculis tortor dapibus cursus. Curabitur quis sapien nec tortor dictum gravida. - -```bash -'Authorization': 'Token ' -``` - -## API Tokens - -Nullam convallis mauris at nunc consectetur, ac imperdiet leo rutrum. Maecenas cursus purus a pellentesque blandit. Pellentesque vitae lacinia libero, non mollis metus. - -Nam id ullamcorper urna, at rutrum enim. [Maecenas vulputate](/introduction) vehicula libero, vitae sodales augue pretium nec. Quisque a magna tempor, semper risus vel, fermentum nunc. Pellentesque fermentum interdum ex, eu convallis massa blandit sed. Aliquam bibendum ipsum vel laoreet auctor. - -### Permissions - -Etiam lobortis ut odio ut fermentum. Nunc odio velit, sollicitudin at consectetur id, tristique eget turpis. Aliquam at risus vitae dolor sodales venenatis. In hac habitasse platea dictumst. - -Aenean consequat diam eget mollis fermentum. [Quisque eu malesuada](/introduction) felis, non dignissim libero. diff --git a/sdks/angular.mdx b/sdks/angular.mdx index f68482aa..6ff50c08 100644 --- a/sdks/angular.mdx +++ b/sdks/angular.mdx @@ -1,22 +1,20 @@ --- title: "Angular Component Library" description: "Novu’s Angular library provides an Angular component wrapper over the Notification Center Web that you can use to integrate the notification center into your Angular application. " -icon: 'angular' +icon: "angular" --- - [Explore the source code on GitHub](https://github.com/novuhq/novu/tree/next/packages/notification-center-angular) - ## Installation ```javascript npm install @novu/notification-center-angular ``` - If you're ready to start integrating in your Angular app, jump straight to our [Angular quickstart](/quickstarts/angular) + +  If you're ready to start integrating in your Angular app, jump straight to + our [Angular quickstart](/quickstarts/angular) + Learn more about usage of the [Angular Component in detail.](/notification-center/client/angular) - - - diff --git a/sdks/framework/typescript/client.mdx b/sdks/framework/typescript/client.mdx new file mode 100644 index 00000000..1e60cf81 --- /dev/null +++ b/sdks/framework/typescript/client.mdx @@ -0,0 +1,50 @@ +--- +title: "Client" +--- + +The Novu Framework is an optional Class you can pass to the `serve` function to override some global settings. +By default, we will inject a new instance of the `Client` class in your `serve` method with the following defaults: + +## Client Interface + + + Your Novu Secret Key, used to sign the HMAC header to guarantee the + authenticity of our requests. + + + + This bypasses the HMAC signature verification, required for local development + and testing against [Local Studio](/workflow/studio). + + +## Environment Variables + +Unless specified in the `Client` constructor the `Client` class will look for the following environment variables: + +- `NOVU_SECRET_KEY` - Your Novu Secret Key +- `NOVU_API_URL` - Defaults to `https://api.novu.co`. For EU customers, this should be set to `https://eu.api.novu.co`. + +## Development Environment + +When your service is running in development mode `process.env.NODE_ENV=development`, the following rules will auto apply: + +- `strictAuthentication` will be set to `false`. + +## Code Example + +```typescript +import { Client } from "@novu/framework"; + +const client = new Client({ + secretKey: "my-secret-key", + strictAuthentication: false, +}); +``` diff --git a/sdks/framework/typescript/overview.mdx b/sdks/framework/typescript/overview.mdx new file mode 100644 index 00000000..272c17a4 --- /dev/null +++ b/sdks/framework/typescript/overview.mdx @@ -0,0 +1,54 @@ +--- +title: "Overview" +--- + +import { EchoTerminal } from "/snippets/echo-terminal.mdx"; + +Although you can trigger Novu workflows from any programming language using our Rest API SDKs. + +We believe that the best way to build your notification strategy is to treat your templates and workflows as your Notification Design System. +Building reusable components to be consumed and embedded by your non-technical peers in any combination. + +Typescript SDKs enable the creation of stunning channel content like E-mails using modern technologies like React/Vue/etc... +Treating your emails as a front-end concern opens up a world of possibilities to reuse design tokens, components, and even entire templates across your applications for consistent branding and a cohesive user experience. + +Novu Framework was built and optimized with extreme focus on Developer Experience. +Our `@novu/framework` SDK is written in Typescript, and we recommend using Typescript for your own projects as well. + + + +## Type Safe Workflow Payloads + +When defining a [workflow payload](/framework/concepts/payload) schema, our SDK will automatically infer it to a Typescript interface. + +```typescript +import { workflow } from "@novu/framework"; + +const myWorkflow = workflow( + "new-signup", + async ({ step, payload }) => { + await step.email("send-email", () => { + return { + subject: "Hello World", + // The payload object here is type-safe + body: `Hi ${payload.name}, welcome to our platform!`, + }; + }); + }, + { + payloadSchema: { properties: { name: { type: "string" } } }, + } +); +``` + +## Type Safe Steps + +Similarly, when defining a [step](/framework/concepts/steps) schema, our SDK will automatically infer it to a Typescript interface. + +## Step Controls + +Build and define type safe controls to expose no-code editing capabilities to your teammates. + +## Explore the SDK + +- Client diff --git a/sdks/framework/typescript/steps/chat.mdx b/sdks/framework/typescript/steps/chat.mdx new file mode 100644 index 00000000..0cf1fc37 --- /dev/null +++ b/sdks/framework/typescript/steps/chat.mdx @@ -0,0 +1,24 @@ +--- +title: "Chat Channel Step" +sidebarTitle: "Chat" +--- + +## Chat Output + + + ```typescript Delay + await step.chat('chat', async () => { + return { + body: 'A new post has been created', + }; + }); + ``` + + + + The message to be sent to the chat channel. + + +## Chat Result + +The `Chat` step does not return any result object. diff --git a/sdks/framework/typescript/steps/custom.mdx b/sdks/framework/typescript/steps/custom.mdx new file mode 100644 index 00000000..2e0aae43 --- /dev/null +++ b/sdks/framework/typescript/steps/custom.mdx @@ -0,0 +1,39 @@ +--- +title: "Custom Action Step" +sidebarTitle: "Custom" +description: "Used to execute any custom code as a step in the workflow." +--- + + + ```typescript Custom Step + await step.custom('custom', async () => { + return { + hello: 'world', + } + }, { + outputSchema: z.object({ + hello: z.string(), + }), + }); + ``` + + + + ```typescript Custom Step Result + const { hello } = await step.custom('custom', resolver); + ``` + + +## Custom Step Outputs + + + The custom step is a unique step that allows you to specify the output of the + step, the output is strongly typed using the `outputSchema`. + + +## Custom Step Result + + + The custom step returns any object that you responded with in the step + resolver function. + diff --git a/sdks/framework/typescript/steps/delay.mdx b/sdks/framework/typescript/steps/delay.mdx new file mode 100644 index 00000000..eb7ae7be --- /dev/null +++ b/sdks/framework/typescript/steps/delay.mdx @@ -0,0 +1,42 @@ +--- +title: "Delay Action Step" +sidebarTitle: "Delay" +--- + + + ```typescript Delay Output + await step.delay('delay', async () => { + return { + unit: 'day', + amount: 1, + }; + }); + ``` + + + + ```typescript Delay Result + const { duration } = await step.delay('delay-1-week', resolver); + ``` + + +## Step Outputs + + + This combined with the unit field determines the amount of time to digest + events for. + + + + The measurement unit for the amount field. + + +## Step Result + + + The duration of the delay in milliseconds. + diff --git a/sdks/framework/typescript/steps/digest.mdx b/sdks/framework/typescript/steps/digest.mdx new file mode 100644 index 00000000..09e206ac --- /dev/null +++ b/sdks/framework/typescript/steps/digest.mdx @@ -0,0 +1,144 @@ +--- +title: "Digest Action Step" +sidebarTitle: "Digest" +--- + + + ```typescript Regular + await step.digest('digest', async (controls) => { + return { + amount: 1, + unit: 'hours' + } + }); + ``` + ```typescript Scheduled + await step.digest('digest', async (controls) => { + return { + cron: '0 0 * * *', + digestKey: 'postId', + } + }); + ``` + ```typescript Look-back Window + await step.digest('digest', async (controls) => { + return { + amount: 1, + unit: 'hours', + lookBackWindow: { + amount: 10, + unit: 'minutes', + } + } + }); + ``` + + ```typescript Digest Key + await step.digest('digest', async (controls) => { + return { + amount: 1, + unit: 'hours', + digestKey: 'postId', + } + }); + ``` + + + + + ```typescript Digest Result + const { events } = await step.digest('digest', resolver); + + const { + id, + time, + payload + } = events[0]; + ``` + + + +## Digest Step Output + +### Regular Digest + + + This combined with the unit field determines the amount of time to digest + events for. + + + + The measurement unit for the amount field. + + + + This key is used to group events for the digest engine, if not provided all events will be digested together on the subscriber level. + When provided, events will be grouped by the `subscriberId` and the `digestKey`. + + The digest key must match the `payloadSchema` key provided on the workflow. + + + + + An example of a parameter field + + + + An example of a parameter field + + + + An example of a parameter field + + + + + +### Scheduled Digest + + + The [cron expression](https://crontab.guru/) to schedule the digest on a repeating basis. + + `@novu/framework` SDK exports a utility enum called `CronExpression` to help you build cron expressions. + + ```typescript + import { CronExpression } from '@novu/framework'; + + await step.digest('digest', async (controls) => { + return { + cron: CronExpression.EVERY_DAY_AT_1AM + } + }); + ``` + + + + + This key is used to group events for the digest engine, if not provided all + events will be digested together on the subscriber level. When provided, + events will be grouped by the `subscriberId` and the `digestKey`. + + +## Digest Step Result + + + An array of triggers that have been digested. + + + + The transactionId of the digested event + + + The time when the event was triggered + + + + The original payload passed to the digested event + + + + diff --git a/sdks/framework/typescript/steps/email.mdx b/sdks/framework/typescript/steps/email.mdx new file mode 100644 index 00000000..ecd48d6d --- /dev/null +++ b/sdks/framework/typescript/steps/email.mdx @@ -0,0 +1,27 @@ +--- +title: "Email Channel Step" +sidebarTitle: "Email" +--- + + + ```typescript Email + await step.email('email', async () => { + return { + subject: 'You received a message', + body: 'A new post has been created', + }; + }); + ``` + + +## Email Step Output + + + The title or subject email + + + + The HTML body of the email + + +## Email Step Result diff --git a/sdks/framework/typescript/steps/inbox.mdx b/sdks/framework/typescript/steps/inbox.mdx new file mode 100644 index 00000000..1bd61667 --- /dev/null +++ b/sdks/framework/typescript/steps/inbox.mdx @@ -0,0 +1,46 @@ +--- +title: "In-App Channel Step" +sidebarTitle: "Inbox" +--- + + + ```typescript Inbox + await step.inApp('inbox', async () => { + return { + body: 'A new notification has been created', + }; + }); + ``` + + + + ```typescript Inbox + const { + seen, + read, + lastSeenDate, + lastReadDate, + } = await step.inApp('inbox', handler); + ``` + + +## Inbox Step Output + + + The body of the inbox notification + + +## Inbox Step Result + + + The body of the inbox notification + + + The body of the inbox notification + + + The body of the inbox notification + + + The body of the inbox notification + diff --git a/sdks/framework/typescript/steps/introduction.mdx b/sdks/framework/typescript/steps/introduction.mdx new file mode 100644 index 00000000..5252418e --- /dev/null +++ b/sdks/framework/typescript/steps/introduction.mdx @@ -0,0 +1,97 @@ +--- +title: "Step Interface" +--- + + + ```typescript Control Schema + await step.email('stepId', handler, { + controlSchema: z.object({ + subject: z.string(), + components: z.array(z.object({ + type: z.enum(['text', 'button']), + content: z.string(), + })), + }), + }); + ``` + ```typescript Skip + await step.email('stepId', handler, { + skip: async (controls) => true, + }); + ``` + ```typescript Provider Overrides + await step.email('stepId', handler, { + providers: { + slack: ({ controls }) => { + return { + text: 'A new post has been created', + blocks: [{ + type: 'section', + text: { + type: 'mrkdwn', + text: 'A new post has been created', + }, + }], + }; + } + } + }); + ``` + + +## Channel Steps Interface + +All channels follow the same shared interface: + + + This is the unique identifier for the step in the workflow context. It is used + to reference and display the step in the dashboard interface. + + + + This is an async function that returns the content of the step which called + `Outputs`. Each channel has its own output schema. + + + + Additional step configuration. + + +## Options Object + +This is an optional configuration object that defines: [Controls Schema](/framework/concepts/inputs), [Provider Overrides](#provider-overrides), skip and other configurations... + + + A function that returns a boolean value to skip the step. This is helpful when + you want to use previous step results or other custom logic to skip the step + from executing. + + + + This defined the UI Controls exposed in the dashboard for the step. They can + be nested and of any JSON Schema supported structure. + + + + +### Providers Overrides Object + +This object used to access and override the underlying deliver providers SDKs. This is useful when you want to customize the content of the notification with provider unique properties. + +```typescript +type ProvidersOverride = { + [key: ProviderEnum]: ProviderCallback; +}; + +type ProviderCallback = ( + params: ProviderOverridesParams +) => Promise; + +type ProviderOverridesParams = { + controls: StepControls; + output: StepOutput; +}; +``` diff --git a/sdks/framework/typescript/steps/push.mdx b/sdks/framework/typescript/steps/push.mdx new file mode 100644 index 00000000..6b9a4bd4 --- /dev/null +++ b/sdks/framework/typescript/steps/push.mdx @@ -0,0 +1,29 @@ +--- +title: "Push Channel Step" +sidebarTitle: "Push" +--- + + + ```typescript Push + await step.push('push', async () => { + return { + subject: 'You received a message', + body: 'A new post has been created', + }; + }); + ``` + + +## Push Output + + + The title or subject of the provider + + + + The message to be sent to the push channel. + + +## Push Result + +The `Push` step does not return any result object. diff --git a/sdks/framework/typescript/steps/sms.mdx b/sdks/framework/typescript/steps/sms.mdx new file mode 100644 index 00000000..540c6cbc --- /dev/null +++ b/sdks/framework/typescript/steps/sms.mdx @@ -0,0 +1,24 @@ +--- +title: "SMS Channel Step" +sidebarTitle: "SMS" +--- + +## Chat Output + + + ```typescript SMS + await step.sms('chat', async () => { + return { + body: 'A new post has been created', + }; + }); + ``` + + + + The message to be sent to the sms channel. + + +## SMS Result + +The `SMS` step does not return any result object. diff --git a/sdks/framework/typescript/workflow.mdx b/sdks/framework/typescript/workflow.mdx new file mode 100644 index 00000000..8284a5dd --- /dev/null +++ b/sdks/framework/typescript/workflow.mdx @@ -0,0 +1,97 @@ +--- +title: "Digest Action Step" +sidebarTitle: "Workflow" +--- + + + ```typescript Regular + import { workflow } from '@novu/framework'; + + workflow('id', async ({ step, payload }) => { + await step.digest('digest', async () => { + return { + amount: 1, + unit: 'hours', + } + }); + }); + ``` + + ```typescript Context + workflow('id', async (context) => { + const { step, payload, subscriber } = context; + }); + ``` + + ```typescript Payload Schema + workflow('id', handler, { + payloadSchema: z.object({ + hello: z.string(), + }), + }); + ``` + + + +## Workflow Interface + +```typescript +import { workflow } from '@novu/framework'; + +workflow( + workflowId: string, + handler: WorkflowHandler, + options?: WorkflowOptions +): WorkflowInstance; +``` + + + This id should be unique within your organization. + + + + The definition function of the workflow. + + + + An optional options object for workflow level configurations + + + + The schema to validate the event payload against, can be used to provide default values. + + + + + +## Workflow Context + +This context is passed by the workflow engine to provide contextual information about current workflow execution. + + + + + The id of the subscriber, as passed during `/events/trigger` request. + + + Nullable, the first name of the subscriber. + + + Nullable, the last name of the subscriber. + + + + + + The payload of the event that triggered the workflow, will be validated + against the `payloadSchema` if provided. + + + + The object that contains all the step functions, read more at [Step + Functions](/sdks/framework/typescript/steps/introduction). + diff --git a/sdks/go.mdx b/sdks/go.mdx index 6a9ee068..c6ac5510 100644 --- a/sdks/go.mdx +++ b/sdks/go.mdx @@ -1,10 +1,9 @@ --- title: "Go SDK" description: "Connect a Go application to Novu" -icon: 'golang' +icon: "golang" --- - Novu's Go SDK provides simple, yet comprehensive notification management, and delivery capabilities through multiple channels that you can implement using code that integrates seamlessly with your Go application. [Explore the source code on GitHub](https://github.com/novuhq/go-novu) @@ -15,9 +14,12 @@ Novu's Go SDK provides simple, yet comprehensive notification management, and de go get github.com/novuhq/go-novu ``` - If you're ready to start integrating in your Go app, jump straight to our [Go quickstart.](/quickstarts/go) + +  If you're ready to start integrating in your Go app, jump straight to our [Go + quickstart.](/quickstarts/go) + -## Usage +## Usage ```go package main @@ -68,4 +70,3 @@ func main() { fmt.Println(integrations) } ``` - diff --git a/sdks/headless-javascript-service.mdx b/sdks/headless-javascript-service.mdx deleted file mode 100644 index 95245b44..00000000 --- a/sdks/headless-javascript-service.mdx +++ /dev/null @@ -1,15 +0,0 @@ ---- -title: "Headless JavaScript Service" -description: "Novu’s headless library provides users with a lightweight, completely unstyled, zero UI solution for integrating notification functionality into their web applications." -icon: 'js' ---- - -[Explore the source code on GitHub](https://github.com/novuhq/novu/tree/next/packages/headless) - -With just the essential API methods, users can easily incorporate our notification system into any framework or vanilla JavaScript project, without being constrained by our default UI or dependencies. - - -If you're ready to start using the Headless Notification Center, check out our [guide!](/guides/headless-notification-center-guide) - - -Learn more about usage of the [Headless Services in detail.](/notification-center/client/headless/get-started) diff --git a/sdks/iframe-embed.mdx b/sdks/iframe-embed.mdx deleted file mode 100644 index 5ec77d14..00000000 --- a/sdks/iframe-embed.mdx +++ /dev/null @@ -1,9 +0,0 @@ ---- -title: "Iframe Embed Notification Center" -description: "Embed a real-time notification center inside an iframe using our embedded script." -icon: 'html5' ---- - -If you are using a (currently) unsupported client framework, you can use our embedded script. - -Learn more about usage of the [Iframe Embed in detail.](/notification-center/client/iframe) diff --git a/sdks/introduction.mdx b/sdks/introduction.mdx index 2eb0596b..9cd65371 100644 --- a/sdks/introduction.mdx +++ b/sdks/introduction.mdx @@ -1,11 +1,15 @@ --- title: "SDKs & Client Libraries" +sidebarTitle: "Overview" description: "You can interface with Novu's API either over REST, or via libraries for certain languages. Below is a list of our official, supported libraries." --- -It's important to note that our API and backend SDK are intended for use exclusively in server-side applications. -**Attempting to use them in a client-side application will result in Cross-Origin Resource Sharing (CORS) errors.** -This restriction ensures the security and integrity of our services. + + It's important to note that our API and backend SDK are intended for use + exclusively in server-side applications. **Attempting to use them in a + client-side application will result in Cross-Origin Resource Sharing (CORS) + errors.** This restriction ensures the security and integrity of our services. + ## Client Side diff --git a/sdks/java.mdx b/sdks/java.mdx index 888494af..2817c06e 100644 --- a/sdks/java.mdx +++ b/sdks/java.mdx @@ -1,7 +1,7 @@ --- title: "Java SDK" description: "Connect a Java application to Novu" -icon: 'java' +icon: "java" --- Novu's Java SDK provides simple, yet comprehensive notification management, and delivery capabilities through multiple channels that you can implement using code that integrates seamlessly with your Java application. @@ -31,9 +31,12 @@ implementation 'co.novu:novu-java:{latest-version}' Sync your project, and you should have the artifacts downloaded. - If you're ready to start integrating in your Java app, jump straight to our [Java quickstart.](/quickstarts/java) + +  If you're ready to start integrating in your Java app, jump straight to + our [Java quickstart.](/quickstarts/java) + -## Usage +## Usage ```java import co.novu.sdk.Novu; @@ -53,5 +56,5 @@ public class Main { } } -// Sign up on https://web.novu.co and grab your API key from https://web.novu.co/settings +// Sign up on https://dashboard.novu.co and grab your API key from https://dashboard.novu.co/settings ``` diff --git a/sdks/kotlin.mdx b/sdks/kotlin.mdx index db35cde5..2cab5ac3 100644 --- a/sdks/kotlin.mdx +++ b/sdks/kotlin.mdx @@ -1,7 +1,7 @@ --- title: "Kotlin SDK" description: "Connect a Kotlin application to Novu" -icon: 'kotlin' +icon: "kotlin" --- Novu's Kotlin SDK provides simple, yet comprehensive notification management, and delivery capabilities through multiple channels that you can implement using code that integrates seamlessly with your Kotlin application. @@ -19,6 +19,7 @@ Maven users: {use-latest-version} ``` + The latest version can be found [on GitHub.](https://github.com/novuhq/novu-kotlin#installation) Gradle users: @@ -31,9 +32,12 @@ implementation ("co.novu:novu-kotlin:{use-latest-version}") //Kotlin Sync your project, and you should have the artifacts downloaded. - If you're ready to start integrating in your Kotlin app, jump straight to our [Kotlin quickstart.](/quickstarts/kotlin) + +  If you're ready to start integrating in your Kotlin app, jump straight to + our [Kotlin quickstart.](/quickstarts/kotlin) + -## Usage +## Usage ```kotlin diff --git a/sdks/laravel.mdx b/sdks/laravel.mdx index 3164e5a4..142555a8 100644 --- a/sdks/laravel.mdx +++ b/sdks/laravel.mdx @@ -1,10 +1,9 @@ --- title: "Laravel" description: "Connect a Laravel application to Novu" -icon: 'laravel' +icon: "laravel" --- - Novu's Laravel SDK provides simple, yet comprehensive notification management, and delivery capabilities through multiple channels that you can implement using code that integrates seamlessly with your Laravel application. [Explore the source code on GitHub](https://github.com/novuhq/novu-laravel) @@ -15,9 +14,12 @@ Novu's Laravel SDK provides simple, yet comprehensive notification management, a composer require novu/novu-laravel ``` - If you're ready to start integrating in your Laravel app, jump straight to our [Laravel quickstart.]() + +  If you're ready to start integrating in your Laravel app, jump straight to + our [Laravel quickstart.]() + -## Usage +## Usage ```php use Novu\Laravel\Facades\Novu; @@ -41,4 +43,3 @@ novu()->triggerEvent([ ] ])->toArray(); ``` - diff --git a/sdks/nodejs.mdx b/sdks/nodejs.mdx index 20530382..8160793f 100644 --- a/sdks/nodejs.mdx +++ b/sdks/nodejs.mdx @@ -1,7 +1,7 @@ --- title: "NodeJS SDK" description: "Connect a Node.js application to Novu" -icon: 'node' +icon: "node" --- Novu's Node.js SDK provides simple, yet comprehensive notification management, and delivery capabilities through multiple channels that you can implement using code that integrates seamlessly with your Node.js application. @@ -11,28 +11,31 @@ Novu's Node.js SDK provides simple, yet comprehensive notification management, a ## Installation ```javascript -npm install @novu/node +npm install @novu/node ``` - If you're ready to start integrating in your Node.js app, jump straight to our [Nodejs quickstart.](/quickstarts/nodejs) + +  If you're ready to start integrating in your Node.js app, jump straight to + our [Nodejs quickstart.](/quickstarts/nodejs) + -## Usage +## Usage ```javascript -import { Novu } from '@novu/node'; +import { Novu } from "@novu/node"; const novu = new Novu(""); -await novu.trigger('', { +await novu.trigger("", { to: { - subscriberId: '', - email: 'test@email.com', - firstName: 'John', - lastName: 'Doe', + subscriberId: "", + email: "test@email.com", + firstName: "John", + lastName: "Doe", }, payload: { organization: { - logo: 'https://evilcorp.com/logo.png', + logo: "https://evilcorp.com/logo.png", }, }, }); diff --git a/sdks/php.mdx b/sdks/php.mdx index b0ffff78..f521afa5 100644 --- a/sdks/php.mdx +++ b/sdks/php.mdx @@ -1,7 +1,7 @@ --- title: "PHP SDK" description: "Connect a PHP application to Novu" -icon: 'php' +icon: "php" --- Novu's PHP SDK provides simple, yet comprehensive notification management, and delivery capabilities through multiple channels that you can implement using code that integrates seamlessly with your PHP application. @@ -14,16 +14,19 @@ Novu's PHP SDK provides simple, yet comprehensive notification management, and d composer require unicodeveloper/novu ``` - If you're ready to start integrating in your PHP app, jump straight to our [PHP quickstart.](/quickstarts/php) + +  If you're ready to start integrating in your PHP app, jump straight to + our [PHP quickstart.](/quickstarts/php) + -## Usage +## Usage ```php use Novu\SDK\Novu; $novu = new Novu(); -// Sign up on https://web.novu.co and grab your API key from https://web.novu.co/settings +// Sign up on https://dashboard.novu.co and grab your API key from https://dashboard.novu.co/settings $response = $novu->triggerEvent([ 'name' => '', diff --git a/sdks/python.mdx b/sdks/python.mdx index 69ac5b99..35f7aae0 100644 --- a/sdks/python.mdx +++ b/sdks/python.mdx @@ -1,7 +1,7 @@ --- title: "Python SDK" description: "Connect a Python application to Novu" -icon: 'python' +icon: "python" --- Novu's Python SDK provides simple, yet comprehensive notification management, and delivery capabilities through multiple channels that you can implement using code that integrates seamlessly with your Python application. @@ -18,9 +18,12 @@ pip install novu poetry add novu ``` - If you're ready to start integrating in your Python app, jump straight to our [Python quickstart.](/quickstarts/python) + +  If you're ready to start integrating in your Python app, jump straight to + our [Python quickstart.](/quickstarts/python) + -## Usage +## Usage ```python from novu.api import EventApi @@ -28,7 +31,7 @@ from novu.api import EventApi url = "https://api.novu.co" api_key = "" -# You can sign up on https://web.novu.co to get your API key from https://web.novu.co/settings +# You can sign up on https://dashboard.novu.co to get your API key from https://dashboard.novu.co/settings novu = EventApi(url, api_key).trigger( name="digest-workflow-example", # This is the Workflow ID. It can be found on the workflow page. diff --git a/sdks/react.mdx b/sdks/react.mdx deleted file mode 100644 index 50d03c11..00000000 --- a/sdks/react.mdx +++ /dev/null @@ -1,19 +0,0 @@ ---- -title: "React Component" -description: "Novu’s React library provides a fully functional and embeddable notification center component with real-time updates that you can drop into your React application." -icon: 'react' ---- - -[Explore the source code on GitHub](https://github.com/novuhq/novu/tree/next/packages/notification-center) - - -## Installation - -```javascript -npm install @novu/notification-center -``` - - If you're ready to start integrating in your React app, jump straight to our [quickstart](/quickstarts/react) - -Learn more about usage of the [React Component in detail.](/notification-center/client/react/get-started) - diff --git a/sdks/ruby.mdx b/sdks/ruby.mdx index 15802f95..7542cf14 100644 --- a/sdks/ruby.mdx +++ b/sdks/ruby.mdx @@ -1,7 +1,7 @@ --- title: "Ruby SDK" description: "Connect a Ruby application to Novu." -icon: 'ruby' +icon: "ruby" --- Novu's Ruby SDK provides simple, yet comprehensive notification management, and delivery capabilities through multiple channels that you can implement using code that integrates seamlessly with your Ruby application. @@ -14,9 +14,12 @@ Novu's Ruby SDK provides simple, yet comprehensive notification management, and gem install novu ``` - If you're ready to start integrating in your Ruby app, jump straight to our [Ruby quickstart.](/quickstarts/ruby) + +  If you're ready to start integrating in your Ruby app, jump straight to + our [Ruby quickstart.](/quickstarts/ruby) + -## Usage +## Usage ```ruby require 'novu' diff --git a/sdks/vue.mdx b/sdks/vue.mdx index 34f867ff..3835f91d 100644 --- a/sdks/vue.mdx +++ b/sdks/vue.mdx @@ -1,19 +1,20 @@ --- title: "Vue library" description: "Novu’s Vue library provides a Vue component wrapper over the Notification Center Web that you can use to integrate the notification center into your Vue application. " -icon: 'vuejs' +icon: "vuejs" --- - [Explore the source code on GitHub](https://github.com/novuhq/novu/tree/next/packages/notification-center-vue) - ## Installation ```javascript npm install @novu/notification-center-vue ``` - If you're ready to start integrating in your Vue app, jump straight to the [Vue quickstart.](/quickstarts/vue) + +  If you're ready to start integrating in your Vue app, jump straight to the + [Vue quickstart.](/quickstarts/vue) + Learn more about usage of the [Vue Component in detail.](/notification-center/client/vue) diff --git a/sdks/web-component.mdx b/sdks/web-component.mdx deleted file mode 100644 index 6ab19656..00000000 --- a/sdks/web-component.mdx +++ /dev/null @@ -1,19 +0,0 @@ ---- -title: "Web Component Library" -description: "Novu’s Web Component library provides a fully functional and embeddable notification center widget with real-time updates that you can drop into any web application." -icon: 'square-js' ---- - -[Explore the source code on GitHub](https://github.com/novuhq/novu/tree/next/packages/notification-center) - - -## Installation - -You can use the web component in two ways: - -- As an ESM Module -- As a bundled CDN version - - If you're ready to start integrating in your web app, jump straight to the [Web component quickstart.](/quickstarts/vanillajs) - -Learn more about usage of the [Web Component in detail.](/notification-center/client/web-component) diff --git a/self-hosting-novu/aws.mdx b/self-hosting-novu/aws.mdx deleted file mode 100644 index fe1a800f..00000000 --- a/self-hosting-novu/aws.mdx +++ /dev/null @@ -1,52 +0,0 @@ ---- -title: 'AWS' -description: 'Learn how to deploy Novu to AWS' -icon: 'amazon' ---- -Amazon Web Services (AWS) is a cloud computing platform by Amazon, providing a wide range of on-demand services like computing power, storage, and databases over the internet. -It enables businesses and individuals to avoid managing physical infrastructure and offers features for computing, storage, databases, analytics, machine learning, and more. -AWS is known for its scalability, flexibility, and global network of data centers. -Learn more about [AWS](https://docs.aws.amazon.com/) - -## Deploying on EC2 with the Docker Compose File -Deploying Novu on EC2 with Docker Compose involves several steps. - -In general, the steps are as follows: -1. **Set up an EC2 Instance**: Create an instance on AWS EC2. We reccomend a bare minimum of 4 CPU Cores and 16GB of RAM. -2. **Install Docker**: Once you have your instance up and running, SSH into it and install Docker. You can do this by running the following commands: -``` bash -sudo yum update -y sudo amazon-linux-extras install docker; -sudo service docker start sudo usermod -a -G docker -ec2-user -``` -3. **Install Docker Compose**: Docker Compose will allow you to easily manage your application which will consist of multiple Docker containers. -Install it by running: -``` bash -sudo curl -L "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose; -sudo chmod +x /usr/local/bin/docker-compose -``` -4. **Deploy Novu**: Now, clone the Novu repository and navigate to the directory containing the docker-compose.yml file. -To start your Novu instance, simply run: -``` bash -docker-compose up -d -``` - -Please note, there are user reports indicating an issue with volume persistence when deploying Novu on EC2 with Docker Compose. -You can see the discussions about this problem [here](https://github.com/novuhq/novu/issues/3339) as we do not provide official support for these types of deployments. -This is something to be aware of when deploying in this manner, and you may need to adopt additional strategies for data persistence. -Be sure to consult the Novu documentation as well as any relevant EC2 and Docker documentation for any necessary details related to your specific deployment needs. -Additionally, keep in mind that these general steps may not cover all nuances or requirements of your particular use case or the specific configuration of the Novu application. -Always ensure any deployment follows best practices for security, scalability, and maintainability. - - -# 🏗️ This page is still in creation process - -We believe in the power of collaboration. If you have insights to share or would like to contribute to the development of this page, we welcome your input. To get involved, follow these steps: - -**Open an Issue:** If you've identified an area for improvement or have a suggestion, start by opening an issue in our repository. This allows us to track and discuss the proposed changes. - -**Create a Pull Request (PR):** Once you're ready to contribute, fork our repository and create a branch. Implement your changes within this branch and submit a pull request to our **Staging branch**. - -By following these steps, you're helping us enhance the quality and depth of our resources. We appreciate your effort in making this page an even more valuable asset. - -Your contributions drive progress. Let's build together! \ No newline at end of file diff --git a/self-hosting-novu/azure.mdx b/self-hosting-novu/azure.mdx deleted file mode 100644 index a5505c76..00000000 --- a/self-hosting-novu/azure.mdx +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: 'Azure' -description: 'Learn how to deploy Novu to Azure' -icon: 'microsoft' ---- -Azure is Microsoft's cloud computing platform. -It offers a variety of services like virtual machines, web hosting, databases, AI tools, and more. -It lets businesses build, deploy, and manage applications and services without managing physical servers. -Azure is known for its global reach, security features, and scalability. -Learn more about [Azure](https://learn.microsoft.com/en-us/azure/) - -# 🏗️ This page is still in creation process - -We believe in the power of collaboration. If you have insights to share or would like to contribute to the development of this page, we welcome your input. To get involved, follow these steps: - -**Open an Issue:** If you've identified an area for improvement or have a suggestion, start by opening an issue in our repository. This allows us to track and discuss the proposed changes. - -**Create a Pull Request (PR):** Once you're ready to contribute, fork our repository and create a branch. Implement your changes within this branch and submit a pull request to our **Staging branch**. - -By following these steps, you're helping us enhance the quality and depth of our resources. We appreciate your effort in making this page an even more valuable asset. - -Your contributions drive progress. Let's build together! - diff --git a/self-hosting-novu/gcp.mdx b/self-hosting-novu/gcp.mdx deleted file mode 100644 index c3a2aab4..00000000 --- a/self-hosting-novu/gcp.mdx +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: 'GCP' -description: 'Learn how to deploy Novu to GCP' -icon: 'google' ---- -GCP stands for Google Cloud Platform. It's a suite of cloud computing services by Google, offering tools for computing, storage, data analysis, AI, and more. -It helps businesses build, deploy, and manage applications using Google's infrastructure. -Services include virtual machines, storage, databases, AI tools, and networking solutions. -Learn more about [GCP](https://cloud.google.com/docs/) - -# 🏗️ This page is still in creation process - -We believe in the power of collaboration. If you have insights to share or would like to contribute to the development of this page, we welcome your input. To get involved, follow these steps: - -**Open an Issue:** If you've identified an area for improvement or have a suggestion, start by opening an issue in our repository. This allows us to track and discuss the proposed changes. - -**Create a Pull Request (PR):** Once you're ready to contribute, fork our repository and create a branch. Implement your changes within this branch and submit a pull request to our **Staging branch**. - -By following these steps, you're helping us enhance the quality and depth of our resources. We appreciate your effort in making this page an even more valuable asset. - -Your contributions drive progress. Let's build together! \ No newline at end of file diff --git a/self-hosting-novu/introduction.mdx b/self-hosting-novu/introduction.mdx deleted file mode 100644 index 481d1385..00000000 --- a/self-hosting-novu/introduction.mdx +++ /dev/null @@ -1,22 +0,0 @@ ---- -title: 'Introduction' -description: '' -icon: 'code' ---- - -Self-hosting Novu involves setting up and maintaining your communication infrastructure using your own servers. -While this offers full control and customization, -some features which are available only for Novu’s cloud-managed won't be available in a self-hosted environment. - -## Minimum System Requirements -For basic installation, a server with 16GB RAM and 8 CPUs is fine. However, specifications vary depending on requirements and here are sample specifications for handling 1M plus notifications: - -- If everything is running on one machine, then at least 36 vCPU and 64GB of RAM. -- If services are running across containers, then it is recommended to run 3 containers per service, each container having 2 vCPU and 4GB of RAM. -- If you're using Kubernetes, then the K8 system needs to have 36 vCPU and 64GB of RAM at least in the cluster to run the servers. -- On AWS, a c6g.8xlarge instance with 32 vCPU and 64GB of RAM. -- In all cases, you need to also have 2 Redis clusters with the queue one having an Append only log file active (AOL) to prevent instances of job loss during an outage, a MongoDB cluster equivalent to a M20 or greater on Atlas, and 10GB of S3 storage. -- Lastly, the queue Redis instance should be 8GB MINIMUM as there needs to be enough space to handle spikes in notifications or the whole system will brick. - -Note that these specifications are subject to your requirements, the scale of your operations, and should be adjusted as needed. -Github login is not available in self-hosting. If you're self hosting Novu, use your email and password to log into your Novu account! diff --git a/self-hosting-novu/kubernetes.mdx b/self-hosting-novu/kubernetes.mdx deleted file mode 100644 index e789c85b..00000000 --- a/self-hosting-novu/kubernetes.mdx +++ /dev/null @@ -1,30 +0,0 @@ ---- -title: 'Kubernetes' -description: 'Learn how to deploy Novu with K8s' -icon: 'dharmachakra' ---- - - - We do not plan to support any official public kubernetes deployment at this time. - This guide is to help assist advanced users who want to deploy Novu on their own kubernetes cluster. - - -Kubernetes is an open-source platform that automates the deployment, scaling, and management of containerized applications. - -You can find a community driven [helm deployment](https://github.com/novuhq/novu/blob/next/docker/kubernetes/helm/README.md) in our repository. - -## Novu Web Container Does not Run on Kubernetes - -Due to the way we build our web container image, -you will need to build your own image and push it to your own registry. -This is due to us compiling the frontend when the container starts. - -You can follow our [dev-deploy-web workflow](https://github.com/novuhq/novu/blob/next/.github/workflows/dev-deploy-web.yml) -to see how we build our web container image. -You can also use this workflow to build your own image, you will just need to remove all of the steps after the deploy to netlify step. - - - Only need to do this IF you do not allow root only access on your cluster. - - - diff --git a/self-hosting-novu/object-storage.mdx b/self-hosting-novu/object-storage.mdx deleted file mode 100644 index 87e5de46..00000000 --- a/self-hosting-novu/object-storage.mdx +++ /dev/null @@ -1,277 +0,0 @@ ---- -title: "Object Storage (S3, Blob, GCS)" -description: "Using S3 as a storage backend for your application" -icon: "box" ---- - -For local development we use [LocalStack.cloud](https://localstack.cloud/). It is utilized as a local emulation of S3 during development. - -This might be beneficial for testing and development purposes before transitioning to actual S3 service like in a production environment. - -## AWS S3 - -### Configuration - -AWS S3 requires the following environment variables to be set: - -1. S3_REGION: -2. S3_LOCAL_STACK (optional): S3 endpoint to connect to, leave blank if your connecting directly to AWS -3. S3_BUCKET_NAME: Name of the bucket to use -4. STORAGE_SERVICE: Name of the storage service to use, 'AWS' or 'LOCALSTACK' for S3. - -### Bucket Configuration - -In general, access to the S3 bucket is very permissive. - -The following process is used to manage access to the S3 bucket. - -General design: - -- public read access (for logo and email attachments—all data is open for read to anyone) -- write access is via pre-signed url (client-side PUT to S3 after server-side request) -- private write access (get, put, delete) -- objects are written with organisationIdTenancy/objectId (at least for brand logos) - -Specifics for access: - -- CORS access on bucket (this is documented) -- bucket object do not need versioning (as all uploads are deemed unique) -- ACL on buckets is 'public-read' (as part of the pre-signed URLs) -- image uploads (as pre-signed) could also have rules to limit (DENY policy) on extension types (although UI also does this but leaves open a vector) -- AWS (specific): ensure that Allow owenership controls is 'BucketOwnerPreferred' to allow ACL access (rather than role/user) (ie PutObjectAcl) - -### Example Terraform - -```terraform -/** - * This sets up an S3 bucket for notifications assets (eg photos, documents). The bucket is - * PUBLIC for read access and needs to be opened up to the API (which is deployed as an ECS task). - * - * It also needs to be opened up to worker (to create pre-signed urls for uploads) - * - * This bucket is: - * * public read access - * * private write access to the credentials (added onto the user rather than onto the bucket) - */ - -# -# WARNING: this bucket uses IAM Policies and ACL for access -# -# - @see https://binaryguy.tech/aws/s3/iam-policies-vs-s3-policies-vs-s3-bucket-acls/#:~:text=The%20biggest%20advantage%20of%20using,well%20as%20objects%20in%20it. -# - @see https://aws.amazon.com/blogs/security/iam-policies-and-bucket-policies-and-acls-oh-my-controlling-access-to-s3-resources/ -# - @see https://stackoverflow.com/questions/47815526/s3-bucket-policy-vs-access-control-list -# -# -# TF: https://www.terraform.io/docs/providers/aws/r/s3_bucket.html -# AWS: http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingBucket.html -# AWS CLI: http://docs.aws.amazon.com/cli/latest/reference/s3api/create-bucket.html -resource "aws_s3_bucket" "notifications" { - bucket = "${var.environment}-notifications-data" - # removed for upgrade to aws provider 4.0 - # see https://registry.terraform.io/providers/hashicorp/aws/latest/docs/guides/version-4-upgrade#acl-argument - # acl = "private" - - tags = { - Environment = var.environment - Terraform = true - } -} - - -# TF: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block -resource "aws_s3_bucket_public_access_block" "notifications" { - bucket = aws_s3_bucket.notifications.id - block_public_acls = false - # This feature protects your bucket from accidentally getting a policy that would enable public access - # We WANT public access for read - block_public_policy = false - ignore_public_acls = false - restrict_public_buckets = false -} - - -# TF: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_ownership_controls -# this is required when setting the 'public-read' acl—without it it fails on apply -resource "aws_s3_bucket_ownership_controls" "notifications" { - bucket = aws_s3_bucket.notifications.id - - rule { - object_ownership = "BucketOwnerPreferred" - } -} - -# TF: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_acl -resource "aws_s3_bucket_acl" "notifications" { - bucket = aws_s3_bucket.notifications.id - # Defaults to private - # ["private" "public-read" "public-read-write" "authenticated-read" "aws-exec-read" "log-delivery-write"] - acl = "public-read" - - depends_on = [ - aws_s3_bucket_ownership_controls.notifications, - aws_s3_bucket_public_access_block.notifications, - ] -} - -# -# Note: no point on having vesrioning enabled -# - -# -# Restricted CORS policy but increased headers exposed (that could be further restrictred) -# -# TF: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_cors_configuration -# AWS: https://docs.amplify.aws/lib/storage/getting-started/q/platform/js/#amazon-s3-bucket-cors-policy-setup -resource "aws_s3_bucket_cors_configuration" "notifications" { - bucket = aws_s3_bucket.notifications.id - - cors_rule { - allowed_headers = ["*"] - allowed_methods = [ - "GET", - "HEAD", - "PUT", - ] - allowed_origins = ["/* OWN FQDN */"] - expose_headers = [ - "x-amz-server-side-encryption", - "x-amz-request-id", - "x-amz-id-2", - "x-amz-meta-tag", - "ETag" - ] - max_age_seconds = 3000 - } -} - -resource "aws_s3_bucket_policy" "notifications" { - bucket = aws_s3_bucket.notifications.id - policy = data.aws_iam_policy_document.notifications-user.json -} - -# [Data] IAM policy to define S3 permissions -# -# Restricting users files types to avoid problems (note: hand helds love to upload PDFs) -# -# TF: https://www.terraform.io/docs/providers/aws/d/iam_policy_document.html -# AWS: http://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html -# AWS CLI: http://docs.aws.amazon.com/cli/latest/reference/iam/create-policy.html -data "aws_iam_policy_document" "notifications-user" { - statement { - sid = "DenyNonImage" - effect = "Deny" - principals { - identifiers = [aws_iam_user.notifications.arn] - type = "AWS" - } - actions = [ - # NOTE: this is an ACL permissions - "s3:PutObjectAcl", - ] - not_resources = [ - "${aws_s3_bucket.notifications.arn}/*.png", - "${aws_s3_bucket.notifications.arn}/*.jpg", - "${aws_s3_bucket.notifications.arn}/*.jpeg", - "${aws_s3_bucket.notifications.arn}/*.gif" - ] - } - # NOTE: these rules exist on the user policy -/* - statement { - effect = "Allow" - principals { - type = "AWS" - identifiers = [aws_iam_user.notifications.arn] - } - actions = [ - "s3:GetObject", - "s3:DeleteObject", - "s3:ListBucket", - "s3:PutObject", - # this is the magic spice, because the perms are created via ACL (rather than roles) - "s3:PutObjectAcl" - ] - resources = [ - "${aws_s3_bucket.notifications.arn}*/ -/*", - ] - } -*/ -} - -####################### - -# TF: https://www.terraform.io/docs/providers/aws/r/iam_policy.html -# AWS: http://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html -# AWS CLI: http://docs.aws.amazon.com/cli/latest/reference/iam/create-policy.html -resource "aws_iam_policy" "s3" { - name = "ecs-app-${var.environment}-s3" - description = "Access from the ecs tasks to s3 buckets" - policy = data.aws_iam_policy_document.s3.json -} - -# [Data] IAM policy to define S3 permissions -# TF: https://www.terraform.io/docs/providers/aws/d/iam_policy_document.html -# AWS: http://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html -# AWS CLI: http://docs.aws.amazon.com/cli/latest/reference/iam/create-policy.html -data "aws_iam_policy_document" "s3" { - statement { - sid = "" - effect = "Allow" - actions = [ - "s3:DeleteObject", - "s3:GetObject", - "s3:PutObject", - # this is the magic spice, because the perms are created via ACL (rather than roles) - "s3:PutObjectAcl", - "s3:ListBucket" - ] - resources = [ - "arn:aws:s3:::${var.assets_bucket}/*" - ] - } -} - -###################### - -# Attaches a managed IAM policy to an IAM role for the ecs tasks -# TF: https://www.terraform.io/docs/providers/aws/r/iam_role_policy_attachment.html -# AWS: http://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_managed-vs-inline.html -# AWS CLI: http://docs.aws.amazon.com/cli/latest/reference/iam/attach-role-policy.html -resource "aws_iam_role_policy_attachment" "s3" { - role = aws_iam_role.app_task.name # need your role attached to whatever is doing the execution (eg workload task) - policy_arn = aws_iam_policy.s3.arn -} -``` - -## Azure Blob Storage Configuration - -### Configuration - -Azure Blob Storage requires the following environment variables to be set: - -### Required - -1. STORAGE_SERVICE: Name of the storage service to use, 'AZURE' Azure Blob. -2. AZURE_CONTAINER_NAME: The name of the container in your Azure Storage account that you wish to use for your blob storage. -3. AZURE_ACCOUNT_NAME: The name of your Azure Storage account. This is used to form the URL at which your blob storage is accessible. -4. AZURE_ACCOUNT_KEY: The access key for your Azure Storage account. This is used to authenticate requests made against your blob storage. -5. AZURE_HOST_NAME: The host name of your Azure Storage account . This is used to form the URL at which your blob storage is accessible. [Ref](https://learn.microsoft.com/en-us/azure/storage/blobs/storage-quickstart-blobs-nodejs?tabs=managed-identity%2Croles-azure-portal%2Csign-in-azure-cli#upload-blobs-to-a-container). - -### Bucket Configuration - -TBD - -## Google Cloud Storage Configuration - -### Configuration - -Google Cloud Storage requires the following environment variables to be set: - -1. `GCS_BUCKET_NAME`: Name of the bucket to use. -2. `GOOGLE_APPLICATION_CREDENTIALS`: Path to the service account key file. -3. `STORAGE_SERVICE`: Name of the storage service to use, 'GCS' for Google Cloud Storage. - -### Bucket Configuration - -TBD diff --git a/self-hosting-novu/terraform.mdx b/self-hosting-novu/terraform.mdx deleted file mode 100644 index 00ada89a..00000000 --- a/self-hosting-novu/terraform.mdx +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: 'Deploy with Terraform' -description: 'Learn how to deploy Novu using Terraform' -icon: 'deploydog' ---- - -Terraform is an infrastructure as code tool that lets you build, change, and version infrastructure safely and efficiently. This includes low-level components like compute instances, storage, and networking; and high-level components like DNS entries and SaaS features. -Learn more about [Terraform](https://developer.hashicorp.com/terraform?product_intent=terraform). - - -# 🏗️ This page is still in creation process - -We believe in the power of collaboration. If you have insights to share or would like to contribute to the development of this page, we welcome your input. To get involved, follow these steps: - -**Open an Issue:** If you've identified an area for improvement or have a suggestion, start by opening an issue in our repository. This allows us to track and discuss the proposed changes. - -**Create a Pull Request (PR):** Once you're ready to contribute, fork our repository and create a branch. Implement your changes within this branch and submit a pull request to our **Staging branch**. - -By following these steps, you're helping us enhance the quality and depth of our resources. We appreciate your effort in making this page an even more valuable asset. - -Your contributions drive progress. Let's build together! \ No newline at end of file diff --git a/snippets/apikey-warning.mdx b/snippets/apikey-warning.mdx index e1f81974..9d7ea157 100644 --- a/snippets/apikey-warning.mdx +++ b/snippets/apikey-warning.mdx @@ -2,4 +2,5 @@ Enter your API key in the `Authorization` field like the example shown below: E.g `ApiKey 18d2e625f05d80e` - \ No newline at end of file + + diff --git a/snippets/cloud-only-feature.mdx b/snippets/cloud-only-feature.mdx index f32ea4b4..0f3b681f 100644 --- a/snippets/cloud-only-feature.mdx +++ b/snippets/cloud-only-feature.mdx @@ -1,4 +1,4 @@ - This feature is available only in Novu cloud. This feature will not work in community self - hosted version and local environment. - \ No newline at end of file + This feature is available only in Novu cloud. This feature will not work in + community self hosted version and local environment. + diff --git a/snippets/contact-support.mdx b/snippets/contact-support.mdx index 61904bad..9582bae0 100644 --- a/snippets/contact-support.mdx +++ b/snippets/contact-support.mdx @@ -1,3 +1,4 @@ -If your question is not answered here, feel free to reach out us at support@novu.co - \ No newline at end of file + If your question is not answered here, feel free to reach out us at + support@novu.co + diff --git a/snippets/echo-terminal.mdx b/snippets/echo-terminal.mdx index 475268ff..0dcc6908 100644 --- a/snippets/echo-terminal.mdx +++ b/snippets/echo-terminal.mdx @@ -8,5 +8,6 @@ export const EchoTerminal = () => { } return
    - } + +} } diff --git a/snippets/missing-provider.mdx b/snippets/missing-provider.mdx index d14b713b..2495236e 100644 --- a/snippets/missing-provider.mdx +++ b/snippets/missing-provider.mdx @@ -1,5 +1,14 @@ export const MissingProvider = ({ channelName }) => ( - Are we missing a provider you'd like to use for {channelName ? channelName : "this"} channel? Please consider adding a new feature request on github or help us upvoting the existing ones on our public roadmap + Are we missing a provider you'd like to use for{" "} + {channelName ? channelName : "this"} channel? Please consider adding a new + feature request on{" "} + + github + {" "} + or help us upvoting the existing ones on our public{" "} + roadmap -); \ No newline at end of file +); + +; diff --git a/snippets/provider-implementation.mdx b/snippets/provider-implementation.mdx index 4e2fa16e..9c744b1e 100644 --- a/snippets/provider-implementation.mdx +++ b/snippets/provider-implementation.mdx @@ -1,3 +1,5 @@ -If you are looking to use {provider} provider in different way or your usecase is not covered by this documentation, you can always reach out to our team at support@novu.co or via [Discord](https://discord.novu.co) - \ No newline at end of file + If you are looking to use {provider} provider in different way or your usecase + is not covered by this documentation, you can always reach out to our team at + support@novu.co or via [Discord](https://discord.novu.co) + diff --git a/snippets/quickstart/deploy.mdx b/snippets/quickstart/deploy.mdx new file mode 100644 index 00000000..2a5bc2b9 --- /dev/null +++ b/snippets/quickstart/deploy.mdx @@ -0,0 +1,6 @@ + + Once you have finished refining your first workflow, it’s time to sync your local changes to Novu Cloud. Novu recommends deploying your workflows similarly to how you will deploy the features that generate those notifications using your CI/CD pipeline or our CLI command. + + Read more about [syncing your changes to the cloud](/deployment/production). + + diff --git a/snippets/quickstart/next-steps.mdx b/snippets/quickstart/next-steps.mdx new file mode 100644 index 00000000..6f1cca13 --- /dev/null +++ b/snippets/quickstart/next-steps.mdx @@ -0,0 +1,19 @@ +## Next Steps + + + + Learn how to build workflows with the Novu Framework. + + + Learn more about the Novu Framework SDK + + + Add the Inbox component to your application with our front-end SDK. + + + Deploy your workflows to Development and Production + + diff --git a/snippets/quickstart/packages.mdx b/snippets/quickstart/packages.mdx new file mode 100644 index 00000000..b733adcf --- /dev/null +++ b/snippets/quickstart/packages.mdx @@ -0,0 +1,12 @@ + + +```bash +npm install @novu/framework zod zod-to-json-schema +``` + +This will install the following packages + +- **`@novu/framework`** SDK Package +- **Zod** (Recommended) - For end-to-end type safety for your Payload and Step Controls + + diff --git a/snippets/quickstart/remix-workflow.mdx b/snippets/quickstart/remix-workflow.mdx new file mode 100644 index 00000000..4db97337 --- /dev/null +++ b/snippets/quickstart/remix-workflow.mdx @@ -0,0 +1,22 @@ +```tsx app/novu/workflows.ts +import { workflow } from '@novu/framework'; +import { z } from 'zod'; + +export const testWorkflow = workflow('test-workflow', async ({ step, payload }) => { + await step.email('send-email', async (controls) => { + return { + subject: controls.subject, + body: 'This is your first Novu Email ' + payload.userName, + }; + }, + { + controlSchema: z.object({ + subject: z.string().default('A Successful Test on Novu from {{userName}}'), + }), + }); + }, { + payloadSchema: z.object({ + userName: z.string().default('John Doe'), + }), +}); +``` diff --git a/snippets/quickstart/secret.mdx b/snippets/quickstart/secret.mdx new file mode 100644 index 00000000..86cc1dab --- /dev/null +++ b/snippets/quickstart/secret.mdx @@ -0,0 +1,8 @@ + + Add `NOVU_SECRET_KEY` environment variable to your `.env` + + ```env + NOVU_SECRET_KEY= + ``` + + diff --git a/snippets/quickstart/start-studio.mdx b/snippets/quickstart/start-studio.mdx new file mode 100644 index 00000000..afacfad8 --- /dev/null +++ b/snippets/quickstart/start-studio.mdx @@ -0,0 +1,13 @@ + + The [Local Studio](/workflow/studio) is where you will build your notification workflows and craft the controls that will be + exposed for your non-technical peers to maintain after your workflow is pushed to your Development or Production + environments. + + ```bash + npx novu@latest dev + ``` + + The `dev` command is your go-to command whenever you want to build and preview your changes before syncing to cloud. By default, it will start a secure tunnel that our durable cloud workflow engine will + be able to communicate with, and the [Local Studio](/workflow/studio) web service listening on `http://localhost:2022` + + diff --git a/snippets/quickstart/test.mdx b/snippets/quickstart/test.mdx new file mode 100644 index 00000000..86dd8bfd --- /dev/null +++ b/snippets/quickstart/test.mdx @@ -0,0 +1,6 @@ + + After your {framework} application is up and running, visit the [Local Studio](/workflow/studio) interface that was started on `http://localhost:2022` by running the `npx novu dev` command on the first step. + + The onboarding guide will guide you to send the newly created sample workflow to your e-mail address. + + diff --git a/snippets/quickstart/workflow.mdx b/snippets/quickstart/workflow.mdx new file mode 100644 index 00000000..2cfc7524 --- /dev/null +++ b/snippets/quickstart/workflow.mdx @@ -0,0 +1,22 @@ +```tsx +import { workflow } from '@novu/framework'; +import { z } from 'zod'; + +export const testWorkflow = workflow('test-workflow', async ({ step, payload }) => { + await step.email('send-email', async (controls) => { + return { + subject: controls.subject, + body: 'This is your first Novu Email ' + payload.userName, + }; + }, + { + controlSchema: z.object({ + subject: z.string().default('A Successful Test on Novu from {{userName}}'), + }), + }); + }, { + payloadSchema: z.object({ + userName: z.string().default('John Doe'), + }), +}); +``` diff --git a/snippets/supported-channels.mdx b/snippets/supported-channels.mdx index 9a695ee9..5bc27c7f 100644 --- a/snippets/supported-channels.mdx +++ b/snippets/supported-channels.mdx @@ -1,8 +1,8 @@ | Channel

    | Content Style

    | Custom Variables
    `{{handlebars}}` format | -| --- | --- | :-: | -| **Email** | HTML | ✅ | -| | Visual Editor | ✅ | -| **SMS** | Text | ✅ | -| **Chat** | Text | ✅ | -| **In-App** | Text | ✅ | -| **Push** | Text | ✅ | +| ----------------- | ----------------------- | :------------------------------------------: | +| **Email** | HTML | ✅ | +| | Visual Editor | ✅ | +| **SMS** | Text | ✅ | +| **Chat** | Text | ✅ | +| **In-App** | Text | ✅ | +| **Push** | Text | ✅ | diff --git a/style.css b/style.css index 8bbea0e9..457404f1 100644 --- a/style.css +++ b/style.css @@ -1,19 +1,27 @@ #sidebar { - top: 12rem; + top: 12rem; } #content-side-layout { - top: 14rem; + top: 14rem; } #content-container > div { - padding-top: 14.5rem; + padding-top: 14.5rem; } /* Remove the logo */ +/* #navbar > div#primary-nav + div a[href="/"] { display: none; } + +*/ + +#navbar > div#primary-nav + div a[href="/"]:has(img) { + display: none; +} + #navbar > div#primary-nav + div div.relative { - margin-left: 0; + margin-left: 0; } diff --git a/subscribers/subscribers.mdx b/subscribers/subscribers.mdx deleted file mode 100644 index bf4206eb..00000000 --- a/subscribers/subscribers.mdx +++ /dev/null @@ -1,491 +0,0 @@ ---- -title: 'Subscribers' -icon: 'user' ---- - -In Novu, we call the entities designed to receive notifications as `Subscribers`. Each subscriber is unique and identified by a unique subscriberId. - - -`subscriberId` is a unique identifier used by Novu to keep track of a specific subscriber. -We recommend using the internal unique id your application uses for a specific user. - - -Each subscriber has the following data points: - -- **User Data** - Data stored in the subscriber object that you can easily access in your notification templates. This contains basic info such as first name, last name, avatar, locale, email, and phone. This data is fixed and structured. -- **Custom Data** - Apart from the above fixed structured user data, any unstructured custom data such as user's address, nationality, height, etc can also be stored in the `data` field using key-value pairs. -- **Channel Specific Credentials** - `deviceTokens` required to send push notifications and `webhookUrl` for chat channel providers can also be stored. - -## Subscriber attributes - -| Field | Type | Required | Example | -| :---------------- | :-------------- | :-------------- | :---------------------------------------------------- | -| subscriberId | string | true | b0bea066-f5fe-11ed-b67e-0242ac120002 | -| firstName | string | false | John | -| lastName | string | false | Doe | -| email | string | false | john.doe@domain.org. | -| phone | string | false | +13603963366 | -| locale | string | false | en | -| avatar | string | false | https://example.com/images/avatar.jpg | -| data | object | false | `{"key": "value"}` | - - -## Subscriber response schema - -**Novu subscriber object** - -```json -{ - "_id": "NOVU_GENERATED_SUBSCRIBER_ID", - "_organizationId": "NOVU_GENERATED_ORG_ID", - "_environmentId": "NOVU_GENERATED_ENV_ID", - "firstName": "John", - "lastName": "Doe", - "subscriberId": "subscriberId", - "email": "[john.doe@org.com](mailto:john.doe@org.com)", - "phone": "+98712345670" - "data": { - "custome_key_1" : "custom_value_1", - "custome_key_2" : "custom_value_2" - } - "channels": [ - { - "credentials": { - "deviceTokens": [ - "token1", - "token2" - ] - }, - "_integrationId": "NOVU_GENERATED_INTEGRATION_ID", - "providerId": "fcm" - }, - { - "credentials": { - "webhookUrl": "URL" - }, - "_integrationId": "NOVU_GENERATED_INTEGRATION_ID", - "providerId": "discord" - } - ], - "deleted": false, - "createdAt": "2022-10-13T17:40:53.231Z", - "updatedAt": "2022-10-13T17:41:53.238Z", - "__v": 0, - "isOnline": false, - "lastOnlineAt": "2022-10-13T17:41:53.238Z", - "avatar": "AVATAR_URL", - "id": "NOVU_GENERATED_SUBSCRIBER_ID" -} -``` - -## Create a subscriber - -You can create a subscriber in Novu using the `identify` method provided in Novu's SDK. - -The `identify` method serves the purpose of handling existing "subscriberId" providers within the database. -When a provider with the same subscriberId already exists, this method performs an update operation using the data present in the payload. -In cases where there is no pre-existing entry, the method generates a new one, thus exhibiting an upsert -behavior. - - -It is possible for two subscribers to have the same email address in our system. This is because the `subscriberId` is the unique identifier that distinguishes one subscriber from another. - - -```jsx -import { Novu } from "@novu/node"; - -// Insert your Novu API Key here -const novu = new Novu(""); - -// Create a subscriber on Novu -await novu.subscribers.identify("132", { - email: "john.doe@domain.com", - firstName: "John", - lastName: "Doe", - phone: "+13603963366", -}); -``` - The `phone` parameter must be parsed as a string. - -You should now be able to see your new subscriber on the Novu dashboard. - -You can also add additional fields such as `phone`, `avatar`, and `data`. - - Remember, all user IDs added to Novu need to be unique. - -If you face issues with the subscriber creation only taking the user ID and omitting other fields such as firstName, lastName, etc., you could consider updating the subscriber after creation. - -Please also be aware that although it's currently possible to create a subscriber with an empty `subscriberId`, this subscriber cannot be deleted later. - -We support creating new subscriber using two ways, `Ahead of Trigger` means adding subscribers before triggering notification or `Inline of Trigger` means sending complete subscriber data in `to` field while triggering. - -### 1. Ahead of Trigger - -Create the subscriber and then trigger the notification to this subscriber. Here `subscriberId` is the required field and other fields are optional. - - - -```jsx -import { Novu } from '@novu/node'; - -const novu = new Novu(''); - -await novu.subscribers.identify('111', { - email: 'john.doe@domain.com', - firstName: 'John', - lastName: 'Doe', - phone: '+13603963366', - avatar: 'https://example.com/images/avatar.jpg', - locale: 'en-US', - data: { customKey1: 'customVal1', customKey2: 'customVal2' }, -}); -``` - - - -```php -use Novu\SDK\Novu; - -$novu = new Novu(''); - -$novu->createSubscriber([ - 'subscriberId' => '111', - 'email' => 'john.doe@domain.com', - 'firstName' => 'John', - 'lastName' => 'Doe', - 'phone' => '+13603963366', - 'avatar' => 'https://example.com/images/avatar.jpg', - 'locale' => 'en', - 'data' => [ - 'customKey1' => 'customVal1', - 'customKey2' => 'customVal2' - ] -]); -``` - - - -Novu will create a subscriber if one does not exist and will update the existing subscriber based on the `identify` payload. You can call this function during registration or signup to make sure the subscriber data is up-to-date, if you wish to save additional attributes with a subscriber, you can pass additional custom data in the **data** field as key-value pairs. - -### 2. Inline of Trigger - -A non-existing subscriber can be added by sending subscriber data in `to` field of the trigger method. If any subscriber with provided `subscriberId` does not exists, a new subscriber will be created. In this case, subscriber will be created first and then the trigger will be executed synchronously. - - - -```jsx -import { Novu } from '@novu/node'; - -const novu = new Novu(''); - -await novu.trigger('', { - to: { - subscriberId: '111', - email: 'john.doe@domain.com', - firstName: 'John', - lastName: 'Doe', - phone: '+13603963366', - }, - payload: { - customVariable: 'variableValue', - organization: { - logo: 'https://organization.com/logo.png', - }, - }, -}); -``` - - -```php -use Novu\SDK\Novu; - -$novu = new Novu(''); - -$novu->triggerEvent([ - 'name' => '', - 'to' => [ - 'subscriberId' => '111', - 'email' => 'john.doe@domain.com', - 'firstName' => 'John', - 'lastName' => 'Doe', - 'phone' => '+13603963366' - ] - 'payload' => [ - 'customVariable' => 'variableValue', - 'organization' => [ - 'logo' => 'https://organization.com/logo.png', - ] - ], -]); -``` - - - -## Bulk Subscriber Creation - -You can create subscribers in bulk *(up to 500 at once)* via the SDKs or [API](/api-reference/subscribers/bulk-create-subscribers). - - The bulk subscriber creation feature is only available in v0.19.0 and above. - - - -```javascript -await novu.subscribers.bulkCreate([ - { - subscriberId: 'test-subscriber-1', - email: 'test-user@sd.com', - firstName: 'subscriber-1', - lastName: 'test-1', - }, - { - subscriberId: 'test-subscriber-2', - email: 'test-user-2@sd.com', - firstName: 'subscriber-2', - lastName: 'test-2', - }, - { - subscriberId: 'test-subscriber-3', - }, -]); -``` - - - -## Import subscribers - -Here, `mock_data.csv` file is a CSV file having mock data. The below script will read this CSV file and create a new subscriber in Novu. Change example variables values like `API_KEY` value with your valid values. Download `mock_data.csv` file from [here](https://drive.google.com/file/d/159Upw30ZcPa_QnzlKF-C6_dHKDJeU-TW/view). - - `mock_data.csv` file should be in the same directory of script - -Steps: - -1. Export your user details in CSV format. -2. Clean this exported CSV file. Keep only relevant fields. -3. Move this CSV file to the script directory. -4. Change the file name in the script with your file name. -5. Run the script. (Make script.sh executable before using) - - - - -```shell -#!/bin/bash - -# Change these variables with your own values -CSV_FILE="mock_data.csv" -API_ENDPOINT="https://api.novu.co/v1/subscribers" -API_KEY="ApiKey " - -OLDIFS=$IFS -IFS=',' -[ ! -f $CSV_FILE ] && { echo "$CSV_FILE file not found"; exit 99; } - -SECONDS=0 - -# Read the CSV file line by line -while read subscriberId firstName lastName email phone locale avatar -do - # Check if subscriberId is empty or not present - if [[ -z "$subscriberId" ]]; then - echo "Skipping row with missing subscriberId" - continue - fi - - # Construct the JSON body for the POST request - json_body="{\"subscriberId\": \"$subscriberId\"" - - # Add optional fields to the JSON body if present - if [[ -n "$firstName" ]]; then - json_body="$json_body, \"firstName\": \"$firstName\"" - fi - - if [[ -n "$lastName" ]]; then - json_body="$json_body, \"lastName\": \"$lastName\"" - fi - - if [[ -n "$email" ]]; then - # Validate email using regex - if [[ ! "$email" =~ ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$ ]]; then - echo "Invalid email format for subscriberId: $subscriberId" - continue - fi - json_body="$json_body, \"email\": \"$email\"" - fi - - if [[ -n "$phone" ]]; then - json_body="$json_body, \"phone\": \"$phone\"" - fi - - if [[ -n "$locale" ]]; then - # Validate locale using regex - if [[ ! "$locale" =~ ^[a-zA-Z]{2}(-[a-zA-Z]{2})?$ ]]; then - echo "Invalid locale format for subscriberId: $subscriberId" - continue - fi - json_body="$json_body, \"locale\": \"$locale\"" - fi - - if [[ -n "$avatar" ]]; then - json_body="$json_body, \"avatar\": \"$avatar\"" - fi - - if [[ -n "$address" || -n "$job" ]]; then - json_body="$json_body, \"data\": {" - - if [[ -n "$address" ]]; then - json_body="$json_body \"address\": \"$address\"" - else - json_body="$json_body \"address\": \"\"" - fi - - if [[ -n "$job" ]]; then - json_body="$json_body, \"job\": \"$job\"" - else - json_body="$json_body, \"job\": \"\"" - fi - - json_body="$json_body }" - fi - - json_body="$json_body}" - - echo $json_body - - #Send the cURL POST request with the JSON body and handle exceptions - response=$(curl -s -X POST -H "Content-Type: application/json" -H "Authorization: $API_KEY" -d "$json_body" -w "%{http_code}" "$API_ENDPOINT") - - http_code=${response:${#response}-3} - - if [[ $http_code -eq 201 ]]; then - echo "cURL request successful for subscriberId: $subscriberId - HTTP Status Code: $http_code" - else - echo "cURL request failed for subscriberId: $subscriberId - HTTP Status Code: $http_code - response : $response" - fi - -done < "$CSV_FILE" - -ELAPSED="Time Elapsed: $(($SECONDS / 3600))hrs $((($SECONDS / 60) % 60))min $(($SECONDS % 60))sec" - -echo $ELAPSED -``` - - -```jsx -import { Novu } from '@novu/node'; -require('dotenv').config(); - -const csv = require('csv-parser'); -const fs = require('fs'); - -const novu = new Novu(''); - -fs.createReadStream('mock_data.csv') - .pipe(csv()) - .on('data', async (data) => { - let subscriber = await novu.subscribers.identify(data.subscriberId, { - email: data.email, - firstName: data.firstName, - lastName: data.lastName, - phone: data.phone, - avatar: data.avatar, - locale: data.locale, - data: { address: data.address, job: data.job }, - }); - - console.log(subscriber.data); - }) - .on('end', () => { - console.log('done'); - }); -``` - - - -## Delete a subscriber - -To stop a subscriber from receiving notifications, you can delete the subscriber. This will hard delete the subscriber and means you will not be able to access this subscriber later. You will have to create this subscriber again. - - - - ```jsx -import { Novu } from '@novu/node'; - -const novu = new Novu(''); - -await novu.subscribers.delete('111'); -``` - - -```php -use Novu\SDK\Novu; - -$novu = new Novu(''); - -$novu->deleteSubscriber('111'); -``` - - - -## Update a subscriber - -In some cases, you want to access a subscriber to update a specific user data field or custom data field. For example, when the users change their email address or personal details. - - - -```jsx -import { Novu } from '@novu/node'; - -const novu = new Novu(''); - -await novu.subscribers.update('111', { - // new email - email: 'john@domain.com', - // new phone - phone: '+19874567832', -}); -``` - - -```php -use Novu\SDK\Novu; - -$novu = new Novu(''); - -$novu->updateSubscriber('111', [ - // new email - 'email' => 'john@domain.com', - // new phone - 'phone' => '+19874567832', -])->toArray(); -``` - - - -## Find a subscriber - -Any subscriber can be retrieved using a unique subscriber identifier `subscriberId`. Check the returned subscriber schema [here](#subscriber-response-schema). - - - - -```javascript -import { Novu } from '@novu/node'; - -const novu = new Novu(''); - -await novu.subscribers.get('111'); -``` - - - - -```php -use Novu\SDK\Novu; - -$novu = new Novu(''); - -$novu->getSubscriber('111')->toArray(); -``` - - - diff --git a/techstack.md b/techstack.md deleted file mode 100644 index 1b96fde0..00000000 --- a/techstack.md +++ /dev/null @@ -1,46 +0,0 @@ - -
    - -# Tech Stack File -![](https://img.stackshare.io/repo.svg "repo") [novuhq/docs](https://github.com/novuhq/docs)![](https://img.stackshare.io/public_badge.svg "public") -

    - -
    - -## DevOps (1) - - - - -
    - Git -
    - Git -
    - -
    - -
    -
    - -Generated via [Stack File](https://github.com/marketplace/stack-file) diff --git a/techstack.yml b/techstack.yml deleted file mode 100644 index 01ce6f70..00000000 --- a/techstack.yml +++ /dev/null @@ -1,20 +0,0 @@ -repo_name: novuhq/docs -report_id: 8f0c7d8b9163b6aad53d68ffb856f732 -version: 0.1 -repo_type: Public -timestamp: '2024-01-04T14:58:20+00:00' -requested_by: unicodeveloper -provider: github -branch: staging -detected_tools_count: 1 -tools: -- name: Git - description: Fast, scalable, distributed revision control system - website_url: http://git-scm.com/ - open_source: true - hosted_saas: false - category: Build, Test, Deploy - sub_category: Version Control System - image_url: https://img.stackshare.io/service/1046/git.png - detection_source_url: https://github.com/novuhq/docs - detection_source: Repo Metadata diff --git a/tenants/introduction.mdx b/tenants/introduction.mdx deleted file mode 100644 index 307f58dc..00000000 --- a/tenants/introduction.mdx +++ /dev/null @@ -1,169 +0,0 @@ ---- -title: "Tenants" -description: "Learn all about Tenants" -icon: 'house' ---- - - - -Multi-tenancy is a common use case for a lot of companies that have multiple organizations that use their applications. In some cases, there is a need to separate the behavior of the notification system depending on the individual tenants. - -Currently, Novu supports the following customizations: - -- Workflow level customizations -- Integration customizations - -## Tenant Management - -Tenants can be created and modified via the [API](http://docs.novu.co/api-reference/tenants/get-tenants) or [Web](https://web.novu.co/tenants?utm_campaign=docs-tenants). Each tenant can have multiple fields on it: - -* Identifier - The identifier is a unique value, and can be used later when pointing to this tenant during trigger calls. -* Name - A human-readable name of the tenant. -* Data - A custom data object that can store information about the tenant. This data can be later accessed inside workflows. - -To create a tenant on the Novu web platform, follow these steps: - -- Click on `Tenants` on the left sidebar. -- In the Tenants section, click on the `Add a tenant` button. -- Set a `Name` and `Tenant identifier`. The tenant identifier is automatically set once you add a name. However, you can modify it to your needs. -- Set a JSON-format `Custom properties` (optional). - -## Workflow Level Customizations - -When triggering a workflow, it is possible to pass the tenant information (id or object, in case of an object, novu will upsert the tenant if it’s not existing) like so: - - - - ```javascript - import { Novu } from '@novu/node'; - - const novu = new Novu(""); - - await novu.trigger('', - { - to: { - subscriberId: '', - }, - payload: { - name: "Hello World", - }, - actor: "actorId" - tenant: "tenantIdentifier" - } - ); - ``` - - - ```php - use Novu\SDK\Novu; - - $novu = new Novu(''); - - $novu->triggerEvent([ - 'name' => '', - 'payload' => [ - 'name' => 'Hello' - ], - 'to' => [ - 'subscriberId' => '' - ], - 'actor': "actorId" - 'tenant': "tenantIdentifier" - ]); - ``` - - - -The tenant information can now be used inside the workflow template in the following way: - -``` -{{ tenant.data.customProp }} -``` - -## Workflow Settings Override - -Workflow settings can now be updated on the workflow per tenant. This enables each tenant to have unique preferences that doesn't affect others within the same workflow. - -The `active` and `channel` preferences fields of a workflow can be updated to be tenant-specific the [workflow-overrides API endpoint](/api-reference/workflow-overrides/create-workflow-overrides). - -This feature is only available from v0.22.0. - -## Integration Level Customizations - -When creating an integration, you can create a condition where you can specify a delivery provider for a channel when a particular tenant id is matched. - -The delivery provider specified will only be used if the trigger is executed with the tenant id. - - - - - -## Tenant-specific Notification Content - -When creating a step, variants could be created for each step including digest and delay. For each variant, conditions set can be applied on when this variant could be used. - -Using so different workflow configurations (for example digest duration) could be configured on the individual workflow for a given tenant. - -For example, if you want a specific notification content to be sent to a tenant named "JohnDoe", and another to be sent to others. You can do the following: - -- Create an email step in your workflow -- In the email step workflow, click on the "Add variant" icon at the top right to add a new variant. - - -- It opens up the condition page. Set the tenant identifier to equal "JohnDoe". - - -- Apply Conditions as shown above. -- Check the email step in the workflow, you'll see the **root variant** and **v1 variant**. - - -Once a trigger runs, the Novu engine checks to see if there's a `tenant` identifier specified in the trigger code. - -An example of the trigger code: - - - - ```javascript - import { Novu } from '@novu/node'; - - const novu = new Novu(""); - - await novu.trigger('', - { - to: { - subscriberId: '', - }, - tenant: "JohnDoe" - } - ); - ``` - - - ```php - use Novu\SDK\Novu; - - $novu = new Novu(''); - - $novu->triggerEvent([ - 'name' => '', - 'to' => [ - 'subscriberId' => '' - ], - 'tenant': "JohnDoe" - ]); - ``` - - - -- If it exists, a condition check is done to see if there's a variant that matches the tenant identifier. -- If there's a match, the specific variant template will be fired. -- If there's no match, the root variant template will be fired. - - -It is also possible to use handlebars to have minor tenant adjustments in a single step: - -``` - -``` - - For self-hosted users, this is only available from version 0.22.0. diff --git a/topnav.js b/topnav.js index 6d978214..18ae391e 100644 --- a/topnav.js +++ b/topnav.js @@ -1,10 +1,19 @@ const topNavLinks = [ - { label: "Pricing", url: "https://novu.co/pricing/?utm_campaign=docs_top_nav" }, - { label: "Blog", url: "https://novu.co/blog/?utm_campaign=docs_top_nav" }, - { label: "Contact us", url: "https://novu.co/contact-us/?utm_campaign=docs_top_nav" } + { + label: "Pricing", + url: "https://novu.co/pricing/?utm_campaign=docs_top_nav", + }, + { label: "Blog", url: "https://novu.co/blog/?utm_campaign=docs_top_nav" }, + { + label: "Contact us", + url: "https://novu.co/contact-us/?utm_campaign=docs_top_nav", + }, ]; -const topNavCta = { label: "Get Started", url: "https://web.novu.co?utm_campaign=docs_top_bar_gs" }; +const topNavCta = { + label: "Get Started", + url: "https://dashboard.novu.co?utm_campaign=docs_top_bar_gs", +}; const darkLogo = ` `; +const navbar = document.getElementById("navbar"); -const navbar = document.getElementById('navbar'); - -const navItemClassStyles = 'font-medium text-gray-600 dark:text-gray-400 hover:border-b-[1.5px] hover:border-gray-200 dark:hover:border-gray-700 hover:text-gray-800 dark:hover:text-gray-300' +const navItemClassStyles = + "font-medium text-gray-600 dark:text-gray-400 hover:border-b-[1.5px] hover:border-gray-200 dark:hover:border-gray-700 hover:text-gray-800 dark:hover:text-gray-300"; // Create the navigation component -const navComponent = document.createElement('div'); -navComponent.id = 'primary-nav'; +const navComponent = document.createElement("div"); +navComponent.id = "primary-nav"; navComponent.innerHTML = ` -
    +