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

# Subscribers

In Novu, we call the entities designed to receive notifications as `Subscribers`. Each subscriber is unique and identified by a unique subscriberId.

<Note>
  `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.
</Note>

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](mailto:john.doe@domain.org).                             |
| phone        | string | false    | +13603963366                                                                   |
| locale       | string | false    | en                                                                             |
| avatar       | string | false    | [https://example.com/images/avatar.jpg](https://example.com/images/avatar.jpg) |
| data         | object | false    | `{"key": "value"}`                                                             |

## Subscriber response schema

**Novu subscriber object**

```json theme={null}
{
  "_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.

<Tip>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 <Tooltip tip="A combination of the words “update” and “insert”">upsert</Tooltip>
behavior.</Tip>

<Note>
  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.
</Note>

```jsx theme={null}
import { Novu } from "@novu/node";

// Insert your Novu API Key here
const novu = new Novu("<NOVU_API_KEY>");

// Create a subscriber on Novu
await novu.subscribers.identify("132", {
  email: "john.doe@domain.com",
  firstName: "John",
  lastName: "Doe",
  phone: "+13603963366",
});
```

<Warning> The `phone` parameter must be parsed as a string.</Warning>

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`.

<Note> Remember, all user IDs added to Novu need to be unique. </Note>

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.

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

    const novu = new Novu('<NOVU_API_KEY>');

    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' },
    });
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    use Novu\SDK\Novu;

    $novu = new Novu('<NOVU_API_KEY>');

    $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'
        ]
    ]);
    ```
  </Tab>
</Tabs>

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.

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

    const novu = new Novu('<NOVU_API_KEY>');

    await novu.trigger('<WORKFLOW_TRIGGER_IDENTIFIER>', {
      to: {
        subscriberId: '111',
        email: 'john.doe@domain.com',
        firstName: 'John',
        lastName: 'Doe',
        phone: '+13603963366',
      },
      payload: {
        customVariable: 'variableValue',
        organization: {
          logo: 'https://organization.com/logo.png',
        },
      },
    });
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    use Novu\SDK\Novu;

    $novu = new Novu('<NOVU_API_KEY>');

    $novu->triggerEvent([
      'name' => '<WORKFLOW_TRIGGER_IDENTIFIER>',
      'to' => [
          'subscriberId' => '111',
          'email' => 'john.doe@domain.com',
          'firstName' => 'John',
          'lastName'  => 'Doe',
          'phone' => '+13603963366'
      ]
      'payload' => [
          'customVariable' => 'variableValue',
          'organization' => [
            'logo' => 'https://organization.com/logo.png',
          ]
        ],
    ]);
    ```
  </Tab>
</Tabs>

## 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).

<Note> The bulk subscriber creation feature is only available in v0.19.0 and above.</Note>

<Tabs>
  <Tab title="Node.js">
    ```javascript theme={null}
    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',
      },
    ]);
    ```
  </Tab>
</Tabs>

## 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).

<Note> `mock_data.csv` file should be in the same directory of script </Note>

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)

<Tabs>
  <Tab title="Shell">
    ```shell theme={null}
    #!/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 <API_KEY>"

    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
    ```
  </Tab>

  <Tab title="Node.js">
    ```jsx theme={null}
    import { Novu } from '@novu/node';
    require('dotenv').config();

    const csv = require('csv-parser');
    const fs = require('fs');

    const novu = new Novu('<NOVU_API_KEY>');

    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');
      });
    ```
  </Tab>
</Tabs>

## 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.

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

    const novu = new Novu('<NOVU_API_KEY>');

    await novu.subscribers.delete('111');
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    use Novu\SDK\Novu;

    $novu = new Novu('<NOVU_API_KEY>');

    $novu->deleteSubscriber('111');
    ```
  </Tab>
</Tabs>

## 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.

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

    const novu = new Novu('<NOVU_API_KEY>');

    await novu.subscribers.update('111', {
      // new email
      email: 'john@domain.com',
      // new phone
      phone: '+19874567832',
    });
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    use Novu\SDK\Novu;

    $novu = new Novu('<NOVU_API_KEY>');

    $novu->updateSubscriber('111', [
        // new email
        'email' => 'john@domain.com',
        // new phone
        'phone' => '+19874567832',
    ])->toArray();
    ```
  </Tab>
</Tabs>

## Find a subscriber

Any subscriber can be retrieved using a unique subscriber identifier `subscriberId`. Check the returned subscriber schema [here](#subscriber-response-schema).

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

    const novu = new Novu('<NOVU_API_KEY>');

    await novu.subscribers.get('111');
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    use Novu\SDK\Novu;

    $novu = new Novu('<NOVU_API_KEY>');

    $novu->getSubscriber('111')->toArray();
    ```
  </Tab>
</Tabs>
