> ## Documentation Index
> Fetch the complete documentation index at: https://apidocs.royalti.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Notification System Guide

> Complete guide to managing notifications and notification preferences in Royalti.io - receive updates through multiple channels

## Overview

The Royalti.io notification system keeps users informed about important events across your workspace through multiple delivery channels. This guide covers how to retrieve, manage, and customize notification preferences for your users.

### Why Use Notifications?

* **Stay Informed**: Get real-time updates about critical events in your workspace
* **Multi-Channel Delivery**: Receive notifications via in-app, email, and push notifications
* **Customizable**: Control which events trigger notifications and through which channels
* **Centralized**: All notifications are accessible through a unified API
* **Actionable**: Notifications link directly to relevant resources for quick action

***

## Notification Channels

Royalti.io supports three notification channels:

| Channel  | Description                                                 | Use Case                                              |
| :------- | :---------------------------------------------------------- | :---------------------------------------------------- |
| `IN_APP` | In-app notifications displayed in the Royalti dashboard     | Real-time updates while users are actively working    |
| `EMAIL`  | Email notifications sent to user's registered email address | Important updates that require user attention         |
| `PUSH`   | Push notifications sent to mobile devices                   | Urgent updates when users are away from the dashboard |

Users can customize which channels they want to receive for each notification type through their preferences.

***

## Notification Types

Royalti.io generates notifications for events across multiple categories:

### Royalty File Processing

| Event Type                       | Description                        | Default Channels |
| :------------------------------- | :--------------------------------- | :--------------- |
| `ROYALTY_FILE_UPLOADED`          | Royalty file uploaded successfully | IN\_APP          |
| `ROYALTY_FILE_PROCESSED`         | Royalty file processing completed  | IN\_APP, EMAIL   |
| `ROYALTY_FILE_PROCESSING_FAILED` | Royalty file processing failed     | IN\_APP, EMAIL   |
| `ROYALTY_FILE_DELETED`           | Royalty file deleted               | IN\_APP          |

### User & Collaboration Events

| Event Type                | Description                          | Default Channels |
| :------------------------ | :----------------------------------- | :--------------- |
| `USER_CREATED`            | New user added to workspace          | IN\_APP          |
| `USER_UPDATED`            | User profile or permissions modified | IN\_APP          |
| `USER_DEACTIVATED`        | User account deactivated             | IN\_APP          |
| `USER_DELETED`            | User removed from workspace          | IN\_APP, EMAIL   |
| `USER_INVITATION_SENT`    | User invited to workspace            | IN\_APP, EMAIL   |
| `USER_ADDED_TO_SPLIT`     | User added to revenue split          | IN\_APP, EMAIL   |
| `USER_REMOVED_FROM_SPLIT` | User removed from revenue split      | IN\_APP          |

### Artist Management

| Event Type                 | Description                                    | Default Channels |
| :------------------------- | :--------------------------------------------- | :--------------- |
| `ARTIST_CREATED`           | New artist profile created                     | IN\_APP          |
| `ARTIST_UPDATED`           | Artist profile modified                        | IN\_APP          |
| `ARTIST_DEACTIVATED`       | Artist profile deactivated                     | IN\_APP          |
| `ARTIST_DELETED`           | Artist profile removed                         | IN\_APP, EMAIL   |
| `ARTIST_AUTO_CREATED`      | Artist automatically created during processing | IN\_APP          |
| `ARTIST_RESOLUTION_FAILED` | Failed to resolve artist information           | IN\_APP          |

### Catalog Management (Tracks & Releases)

| Event Type            | Description                         | Default Channels |
| :-------------------- | :---------------------------------- | :--------------- |
| `ASSET_CREATED`       | New asset (track) added             | IN\_APP          |
| `ASSET_UPDATED`       | Asset modified                      | IN\_APP          |
| `ASSET_DEACTIVATED`   | Asset deactivated                   | IN\_APP          |
| `ASSET_DELETED`       | Asset removed                       | IN\_APP, EMAIL   |
| `PRODUCT_CREATED`     | New product (album/release) created | IN\_APP          |
| `PRODUCT_UPDATED`     | Product modified                    | IN\_APP          |
| `PRODUCT_DEACTIVATED` | Product deactivated                 | IN\_APP          |
| `PRODUCT_DELETED`     | Product removed                     | IN\_APP, EMAIL   |

### Financial & Payment Events

| Event Type                 | Description                    | Default Channels |
| :------------------------- | :----------------------------- | :--------------- |
| `PAYMENT_REQUEST_SENT`     | Payment request created        | IN\_APP, EMAIL   |
| `PAYMENT_REQUEST_APPROVED` | Payment request approved       | IN\_APP, EMAIL   |
| `PAYMENT_REQUEST_REJECTED` | Payment request rejected       | IN\_APP, EMAIL   |
| `PAYMENT_MADE_PROCESSING`  | Payment being processed        | IN\_APP          |
| `PAYMENT_MADE_COMPLETED`   | Payment successfully completed | IN\_APP, EMAIL   |
| `PAYMENT_MADE_FAILED`      | Payment failed                 | IN\_APP, EMAIL   |
| `PAYMENT_PROCESSING`       | Payment in processing state    | IN\_APP          |
| `PAYMENT_COMPLETED`        | Payment completed              | IN\_APP          |
| `PAYMENT_DELETED`          | Payment record deleted         | IN\_APP          |
| `REVENUE_CREATED`          | Revenue entry created          | IN\_APP          |
| `REVENUE_UPDATED`          | Revenue entry modified         | IN\_APP          |
| `REVENUE_DELETED`          | Revenue entry deleted          | IN\_APP, EMAIL   |
| `EXPENSE_CREATED`          | Expense entry created          | IN\_APP          |
| `EXPENSE_UPDATED`          | Expense entry modified         | IN\_APP          |
| `EXPENSE_DELETED`          | Expense entry deleted          | IN\_APP, EMAIL   |

### Billing & Subscription

| Event Type        | Description                  | Default Channels |
| :---------------- | :--------------------------- | :--------------- |
| `BILLING_SUCCESS` | Billing operation successful | IN\_APP, EMAIL   |
| `BILLING_ERROR`   | Billing operation failed     | IN\_APP, EMAIL   |
| `BILLING_WARNING` | Billing warning or issue     | IN\_APP, EMAIL   |

### Release Management & Distribution

| Event Type                     | Description                       | Default Channels |
| :----------------------------- | :-------------------------------- | :--------------- |
| `RELEASE_SUBMITTED`            | Release submitted for review      | IN\_APP, EMAIL   |
| `RELEASE_APPROVED`             | Release approved for distribution | IN\_APP, EMAIL   |
| `RELEASE_REJECTED`             | Release rejected                  | IN\_APP, EMAIL   |
| `RELEASE_FEEDBACK_ADDED`       | Feedback added to release         | IN\_APP, EMAIL   |
| `RELEASE_AUTO_CREATED`         | Release automatically created     | IN\_APP          |
| `RELEASE_AUTO_CREATION_FAILED` | Auto-creation of release failed   | IN\_APP          |
| `RELEASE_UPDATED_MANAGEMENT`   | Release updated by management     | IN\_APP          |
| `RELEASE_CREATED`              | Release created                   | IN\_APP          |
| `RELEASE_DELETED`              | Release deleted                   | IN\_APP          |
| `RELEASE_ERROR`                | Release processing error          | IN\_APP, EMAIL   |
| `RELEASE_STATUS_REVERTED`      | Release status reverted           | IN\_APP          |

### File Processing

| Event Type                 | Description               | Default Channels |
| :------------------------- | :------------------------ | :--------------- |
| `FILE_PROCESSING_STARTED`  | File processing initiated | IN\_APP          |
| `FILE_PROCESSING_COMPLETE` | File processing completed | IN\_APP          |
| `FILE_PROCESSING_FAILED`   | File processing failed    | IN\_APP, EMAIL   |

### DDEX Provider Delivery

| Event Type                       | Description                     | Default Channels |
| :------------------------------- | :------------------------------ | :--------------- |
| `PROVIDER_DELIVERY_INITIATED`    | Delivery to provider started    | IN\_APP          |
| `PROVIDER_DELIVERY_COMPLETED`    | Delivery to provider completed  | IN\_APP, EMAIL   |
| `PROVIDER_DELIVERY_FAILED`       | Delivery to provider failed     | IN\_APP, EMAIL   |
| `PROVIDER_DELIVERY_ACKNOWLEDGED` | Provider acknowledged delivery  | IN\_APP          |
| `PROVIDER_STATUS_UPDATE`         | Provider status update received | IN\_APP          |

***

## Default Notification Configuration

When users first sign up or are invited to a workspace, Royalti.io automatically creates smart default notification preferences based on event priority and importance.

### Default Settings Overview

**Philosophy**: In-app notifications for all events, email only for high-priority actions that require attention or represent critical workflow changes.

### Disabled by Default

These low-priority CRUD events are **disabled by default** to reduce notification noise:

| Event Type           | Reason for Disabling                   |
| :------------------- | :------------------------------------- |
| `ARTIST_CREATED`     | Routine catalog management             |
| `ARTIST_UPDATED`     | Frequent updates, low urgency          |
| `ASSET_CREATED`      | High-volume event in active workspaces |
| `ASSET_UPDATED`      | Frequent metadata changes              |
| `PRODUCT_CREATED`    | Routine release management             |
| `EXPENSE_CREATED`    | Financial data entry                   |
| `EXPENSE_UPDATED`    | Routine financial updates              |
| `REVENUE_CREATED`    | Financial data entry                   |
| `REVENUE_UPDATED`    | Routine financial updates              |
| `PAYMENT_PROCESSING` | Intermediate state, not actionable     |
| `PAYMENT_DELETED`    | Administrative action, low urgency     |

**Note**: Users can re-enable these through their notification preferences if needed.

### Email-Enabled by Default

These high-priority events include **both in-app and email notifications** by default:

**Royalty File Processing (Critical)**

* `ROYALTY_FILE_PROCESSED` - Completion requires review
* `ROYALTY_FILE_PROCESSING_FAILED` - Requires immediate attention

**Payment Requests (Action Required)**

* `PAYMENT_REQUEST_SENT` - Approval workflow initiated
* `PAYMENT_REQUEST_APPROVED` - Financial decision confirmed
* `PAYMENT_REQUEST_REJECTED` - Requires follow-up action

**User Collaboration (Important)**

* `USER_INVITATION_SENT` - New team member notification
* `USER_ADDED_TO_SPLIT` - Revenue sharing change

**Deletions (Data Loss Awareness)**

* `ARTIST_DELETED`, `ASSET_DELETED`, `PRODUCT_DELETED`
* `EXPENSE_DELETED`, `REVENUE_DELETED`, `USER_DELETED`

**Release Management (Workflow-Critical)**

* `RELEASE_SUBMITTED` - Distribution workflow step
* `RELEASE_APPROVED`, `RELEASE_REJECTED` - Decision notification
* `RELEASE_FEEDBACK_ADDED` - Requires review

### In-App Only by Default

All other notification types use **in-app notifications only** by default. Users receive these notifications in the dashboard but not via email unless they explicitly enable email delivery.

### Customizing Defaults

**Admin Note**: Default preferences are automatically initialized when:

* A new user signs up
* A user accepts an invitation
* A user logs in for the first time

Users can customize their preferences at any time through the [notification preferences API](#update-notification-preferences).

**Best Practice**: Encourage users to review their notification preferences after joining to ensure they receive the right balance of updates for their workflow.

***

## Quick Start

<Steps>
  <Step title="Get Notifications">
    Retrieve notifications for the authenticated user:

    ```bash theme={null}
    curl -X GET "https://api.royalti.io/notifications?page=1&pageSize=20" \
      -H "Authorization: Bearer YOUR_TOKEN"
    ```

    **Response:**

    ```json theme={null}
    {
      "status": "success",
      "message": "Notifications retrieved successfully",
      "data": {
        "notifications": [
          {
            "id": "dd12f9bc-3ed5-453e-a929-69b6d5267f4b",
            "type": "ASSET_CREATED",
            "title": "Track Created",
            "content": "Track \"Midnight City\" has been created.",
            "resourceType": "Asset",
            "resourceId": "0b13028e-74a9-4d1d-bb9f-12cef06ff236",
            "actionUrl": "/assets/0b13028e-74a9-4d1d-bb9f-12cef06ff236",
            "isRead": false,
            "isImportant": false,
            "createdAt": "2025-08-21T11:42:37.717Z"
          }
        ],
        "total": 34,
        "page": 1,
        "pageSize": 20,
        "totalPages": 2
      }
    }
    ```
  </Step>

  <Step title="Check Unread Count">
    Get the count of unread notifications:

    ```bash theme={null}
    curl -X GET "https://api.royalti.io/notifications/unread/count" \
      -H "Authorization: Bearer YOUR_TOKEN"
    ```

    **Response:**

    ```json theme={null}
    {
      "status": "success",
      "message": "Unread notification count retrieved successfully",
      "data": {
        "count": 5
      }
    }
    ```
  </Step>

  <Step title="Mark as Read">
    Mark a specific notification as read:

    ```bash theme={null}
    curl -X PATCH "https://api.royalti.io/notifications/dd12f9bc-3ed5-453e-a929-69b6d5267f4b/read" \
      -H "Authorization: Bearer YOUR_TOKEN"
    ```

    Or mark all notifications as read:

    ```bash theme={null}
    curl -X PATCH "https://api.royalti.io/notifications/mark-all-read" \
      -H "Authorization: Bearer YOUR_TOKEN"
    ```
  </Step>

  <Step title="Configure Preferences">
    Customize notification preferences:

    ```bash theme={null}
    curl -X PUT "https://api.royalti.io/notifications/preferences" \
      -H "Authorization: Bearer YOUR_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "preferences": [
          {
            "notificationType": "PAYMENT_MADE_COMPLETED",
            "channels": ["EMAIL", "IN_APP", "PUSH"],
            "isEnabled": true
          },
          {
            "notificationType": "ASSET_CREATED",
            "channels": ["IN_APP"],
            "isEnabled": true
          }
        ]
      }'
    ```
  </Step>
</Steps>

***

## Notification Structure

All notifications follow a standardized structure:

```json theme={null}
{
  "id": "dd12f9bc-3ed5-453e-a929-69b6d5267f4b",
  "type": "PAYMENT_MADE_COMPLETED",
  "title": "Payment Completed",
  "content": "Payment of $1,500.00 to Artist Name has been completed successfully.",
  "resourceType": "payment",
  "resourceId": "pay_123abc",
  "actionUrl": "/payments/pay_123abc",
  "isRead": false,
  "isImportant": true,
  "isArchived": false,
  "metadata": {
    "amount": 1500.00,
    "currency": "USD",
    "recipientName": "Artist Name",
    "method": "stripe"
  },
  "expiresAt": null,
  "TenantId": 2,
  "TenantUserId": "c75c54e3-b6e0-4b89-b443-928727b9a931",
  "createdAt": "2025-01-22T14:30:00.000Z",
  "updatedAt": "2025-01-22T14:30:00.000Z"
}
```

### Field Descriptions

| Field          | Type          | Description                                            |
| :------------- | :------------ | :----------------------------------------------------- |
| `id`           | string (UUID) | Unique notification identifier                         |
| `type`         | string        | Notification type from NotificationType enum           |
| `title`        | string        | Short notification title                               |
| `content`      | string        | Detailed notification message                          |
| `resourceType` | string        | Type of resource the notification relates to           |
| `resourceId`   | string        | ID of the related resource                             |
| `actionUrl`    | string        | URL to navigate to when notification is clicked        |
| `isRead`       | boolean       | Whether the notification has been read                 |
| `isImportant`  | boolean       | Whether the notification is marked as important        |
| `isArchived`   | boolean       | Whether the notification is archived                   |
| `metadata`     | object        | Additional event-specific data                         |
| `expiresAt`    | ISO 8601      | When the notification expires (null for no expiration) |
| `TenantId`     | integer       | Workspace ID                                           |
| `TenantUserId` | string (UUID) | User ID the notification is for                        |
| `createdAt`    | ISO 8601      | When the notification was created                      |
| `updatedAt`    | ISO 8601      | When the notification was last updated                 |

***

## API Endpoints

### Get Notifications

Retrieve paginated notifications with optional filtering.

**Endpoint:** `GET /notifications`

**Query Parameters:**

| Parameter    | Type    | Default | Description                 |
| :----------- | :------ | :------ | :-------------------------- |
| `page`       | integer | 1       | Page number                 |
| `pageSize`   | integer | 20      | Number of items per page    |
| `isRead`     | boolean | -       | Filter by read status       |
| `isArchived` | boolean | -       | Filter by archived status   |
| `type`       | string  | -       | Filter by notification type |

**Example:**

```bash theme={null}
# Get unread notifications
curl -X GET "https://api.royalti.io/notifications?isRead=false&page=1&pageSize=10" \
  -H "Authorization: Bearer YOUR_TOKEN"

# Get payment notifications
curl -X GET "https://api.royalti.io/notifications?type=PAYMENT_MADE_COMPLETED" \
  -H "Authorization: Bearer YOUR_TOKEN"
```

***

### Get Unread Count

Get the count of unread notifications for quick badge display.

**Endpoint:** `GET /notifications/unread/count`

**Example:**

```bash theme={null}
curl -X GET "https://api.royalti.io/notifications/unread/count" \
  -H "Authorization: Bearer YOUR_TOKEN"
```

**Response:**

```json theme={null}
{
  "status": "success",
  "message": "Unread notification count retrieved successfully",
  "data": {
    "count": 12
  }
}
```

***

### Mark Notification as Read

Mark a specific notification as read.

**Endpoint:** `PATCH /notifications/{notificationId}/read`

**Path Parameters:**

| Parameter        | Type          | Required | Description            |
| :--------------- | :------------ | :------- | :--------------------- |
| `notificationId` | string (UUID) | Yes      | ID of the notification |

**Example:**

```bash theme={null}
curl -X PATCH "https://api.royalti.io/notifications/dd12f9bc-3ed5-453e-a929-69b6d5267f4b/read" \
  -H "Authorization: Bearer YOUR_TOKEN"
```

***

### Mark All Notifications as Read

Mark all notifications as read for the authenticated user.

**Endpoint:** `PATCH /notifications/mark-all-read`

**Example:**

```bash theme={null}
curl -X PATCH "https://api.royalti.io/notifications/mark-all-read" \
  -H "Authorization: Bearer YOUR_TOKEN"
```

**Response:**

```json theme={null}
{
  "status": "success",
  "message": "All notifications marked as read"
}
```

***

## Notification Preferences

Users can customize which notifications they receive and through which channels.

### Get Notification Preferences

Retrieve the current notification preferences for the authenticated user.

**Endpoint:** `GET /notifications/preferences`

**Example:**

```bash theme={null}
curl -X GET "https://api.royalti.io/notifications/preferences" \
  -H "Authorization: Bearer YOUR_TOKEN"
```

**Response:**

```json theme={null}
{
  "status": "success",
  "message": "Notification preferences retrieved successfully",
  "data": {
    "preferences": [
      {
        "id": "65fdcd80-cada-4c76-83ec-70d8e895774d",
        "notificationType": "PAYMENT_MADE_COMPLETED",
        "channels": ["EMAIL", "IN_APP", "PUSH"],
        "isEnabled": true,
        "createdAt": "2025-05-27T19:55:02.423Z",
        "updatedAt": "2025-05-27T19:55:02.423Z"
      },
      {
        "id": "9e522898-358c-4208-9f7d-94bac43b7cf4",
        "notificationType": "ASSET_CREATED",
        "channels": ["IN_APP"],
        "isEnabled": true,
        "createdAt": "2025-05-27T21:17:56.973Z",
        "updatedAt": "2025-05-27T21:17:56.973Z"
      }
    ]
  }
}
```

***

### Update Notification Preferences

Customize which notifications to receive and through which channels.

**Endpoint:** `PUT /notifications/preferences`

**Request Body:**

```json theme={null}
{
  "preferences": [
    {
      "notificationType": "PAYMENT_MADE_COMPLETED",
      "channels": ["EMAIL", "IN_APP", "PUSH"],
      "isEnabled": true
    },
    {
      "notificationType": "PAYMENT_MADE_FAILED",
      "channels": ["EMAIL", "PUSH"],
      "isEnabled": true
    },
    {
      "notificationType": "ASSET_CREATED",
      "channels": ["IN_APP"],
      "isEnabled": false
    }
  ]
}
```

**Example:**

```bash theme={null}
curl -X PUT "https://api.royalti.io/notifications/preferences" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "preferences": [
      {
        "notificationType": "PAYMENT_MADE_COMPLETED",
        "channels": ["EMAIL", "IN_APP", "PUSH"],
        "isEnabled": true
      },
      {
        "notificationType": "ASSET_CREATED",
        "channels": ["IN_APP"],
        "isEnabled": false
      }
    ]
  }'
```

**Response:**

```json theme={null}
{
  "status": "success",
  "message": "Notification preferences updated successfully",
  "data": {
    "preferences": [
      {
        "id": "65fdcd80-cada-4c76-83ec-70d8e895774d",
        "notificationType": "PAYMENT_MADE_COMPLETED",
        "channels": ["EMAIL", "IN_APP", "PUSH"],
        "isEnabled": true,
        "createdAt": "2025-05-27T19:55:02.423Z",
        "updatedAt": "2025-01-22T15:30:00.000Z"
      }
    ]
  }
}
```

***

## Preference Configuration

### Available Channels

| Channel  | Description                        | Delivery Time                             |
| :------- | :--------------------------------- | :---------------------------------------- |
| `EMAIL`  | Email to registered address        | Immediate or batched (hourly aggregation) |
| `IN_APP` | In-app notification in dashboard   | Real-time                                 |
| `PUSH`   | Push notification to mobile device | Real-time                                 |

### Notification Batching

Some notification types support batching to reduce email volume:

* **Immediate**: Critical notifications (payments, failures) are sent immediately
* **Hourly Aggregation**: Non-critical notifications (asset created, updates) are batched and sent hourly
* **Daily Cleanup**: Expired notifications are automatically removed

***

## Integration Patterns

### Real-Time Notification Display

Implement a notification bell with unread count:

```javascript theme={null}
async function updateNotificationBell() {
  const response = await fetch(
    'https://api.royalti.io/notifications/unread/count',
    {
      headers: {
        'Authorization': `Bearer ${token}`
      }
    }
  );

  const { data } = await response.json();

  // Update UI badge
  document.getElementById('notification-badge').textContent = data.count;
  document.getElementById('notification-badge').style.display =
    data.count > 0 ? 'block' : 'none';
}

// Poll every 30 seconds
setInterval(updateNotificationBell, 30000);
```

***

### Notification List Component

Fetch and display notifications with pagination:

```javascript theme={null}
async function loadNotifications(page = 1) {
  const response = await fetch(
    `https://api.royalti.io/notifications?page=${page}&pageSize=10&isRead=false`,
    {
      headers: {
        'Authorization': `Bearer ${token}`
      }
    }
  );

  const { data } = await response.json();

  data.notifications.forEach(notification => {
    renderNotification(notification);
  });

  // Handle pagination
  renderPagination(data.page, data.totalPages);
}

function renderNotification(notification) {
  const item = document.createElement('div');
  item.className = notification.isImportant ? 'notification important' : 'notification';
  item.innerHTML = `
    <h4>${notification.title}</h4>
    <p>${notification.content}</p>
    <small>${new Date(notification.createdAt).toLocaleString()}</small>
    ${notification.actionUrl ? `<a href="${notification.actionUrl}">View</a>` : ''}
  `;

  item.addEventListener('click', () => markAsRead(notification.id));

  document.getElementById('notification-list').appendChild(item);
}

async function markAsRead(notificationId) {
  await fetch(
    `https://api.royalti.io/notifications/${notificationId}/read`,
    {
      method: 'PATCH',
      headers: {
        'Authorization': `Bearer ${token}`
      }
    }
  );

  // Refresh notification list
  loadNotifications();
}
```

***

### Preference Management UI

Allow users to customize notification preferences:

```javascript theme={null}
async function loadPreferences() {
  const response = await fetch(
    'https://api.royalti.io/notifications/preferences',
    {
      headers: {
        'Authorization': `Bearer ${token}`
      }
    }
  );

  const { data } = await response.json();

  data.preferences.forEach(pref => {
    renderPreferenceControl(pref);
  });
}

async function updatePreference(notificationType, channels, isEnabled) {
  const response = await fetch(
    'https://api.royalti.io/notifications/preferences',
    {
      method: 'PUT',
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        preferences: [
          {
            notificationType,
            channels,
            isEnabled
          }
        ]
      })
    }
  );

  const result = await response.json();
  console.log('Preferences updated:', result);
}
```

***

## Best Practices

**✅ User Experience**

1. **Display unread count** prominently in your UI (bell icon with badge)
2. **Mark notifications as read** when user views them
3. **Provide action links** to relevant resources when applicable
4. **Group similar notifications** to reduce clutter
5. **Auto-refresh** notification count periodically (every 30-60 seconds)
6. **Show timestamps** in user-friendly format (e.g., "5 minutes ago")
7. **Highlight important notifications** with visual indicators
8. **Allow bulk actions** (mark all as read, archive, etc.)

**✅ Performance Optimization**

1. **Use pagination** to limit the number of notifications loaded at once
2. **Cache unread count** to reduce API calls
3. **Implement lazy loading** for notification details
4. **Batch preference updates** when changing multiple settings
5. **Use WebSockets** or polling for real-time updates (if available)

**✅ Preference Management**

1. **Provide granular controls** for each notification type
2. **Set sensible defaults** based on notification importance
3. **Allow channel-specific preferences** (email vs. in-app vs. push)
4. **Explain notification types** so users understand what they're opting into
5. **Respect user preferences** immediately - no delay in applying changes

**⚠️ Common Pitfalls**

1. **Don't overwhelm users** with too many notifications
2. **Don't ignore unread status** - always mark as read when viewed
3. **Don't hardcode notification types** - use the API to get available types
4. **Don't skip error handling** - API calls may fail
5. **Don't forget to implement pagination** - notification lists can grow large

***

## Troubleshooting

### Notifications Not Appearing

**✅ Checklist:**

1. Check notification preferences - is the event type enabled?
2. Verify the user has the correct permissions
3. Check if notifications are filtered by `isRead` or `isArchived`
4. Ensure the tenant ID matches the authenticated user's workspace
5. Check the API response for errors

### Missing Notification Preferences

**✅ Solutions:**

1. Preferences are created on-demand when first updated
2. Default preferences are used if none are set
3. Call `GET /notifications/preferences` to see current state
4. Update preferences with `PUT /notifications/preferences`

### High Unread Count

**✅ Management:**

1. Implement "Mark all as read" functionality
2. Allow users to archive old notifications
3. Set expiration dates for less important notifications
4. Encourage users to customize preferences to reduce noise

***

## FAQ

<AccordionGroup>
  <Accordion title="Can I receive notifications for events from other users in my workspace?">
    Yes, notifications are workspace-scoped. When events occur in your workspace (e.g., a teammate creates an asset), all relevant users receive notifications based on their preferences and permissions.
  </Accordion>

  <Accordion title="How long are notifications stored?">
    Notifications are stored indefinitely unless they have an `expiresAt` date. Expired notifications are automatically removed by the daily cleanup job at 2 AM UTC.
  </Accordion>

  <Accordion title="Can I disable all notifications?">
    Yes, you can disable notifications by setting `isEnabled: false` for each notification type in your preferences. Alternatively, you can choose to only enable the `IN_APP` channel and manage notifications within the dashboard.
  </Accordion>

  <Accordion title="Are notifications sent in real-time?">
    In-app and push notifications are sent in real-time. Email notifications for non-critical events may be batched and sent hourly to reduce email volume. Critical notifications (payments, failures) are always sent immediately.
  </Accordion>

  <Accordion title="Can I get notifications for specific artists or products only?">
    Currently, notifications are workspace-level and cannot be filtered by specific resources. You can control notification types and channels, but not individual resources. This feature may be added in future updates.
  </Accordion>

  <Accordion title="What happens if notification delivery fails?">
    Failed notifications are automatically retried based on the channel's retry strategy. If all retry attempts fail, the notification is logged for debugging but the in-app notification remains accessible through the API.
  </Accordion>

  <Accordion title="Can I customize notification templates?">
    Notification templates are currently managed by Royalti.io and cannot be customized per workspace. However, you can control which notifications you receive through your preferences.
  </Accordion>
</AccordionGroup>

***

## Support & Resources

**API Documentation:**

* [Notifications API Reference](/api-reference/notifications) - Complete API documentation

**Related Guides:**

* [User Management](/guides/user-management) - Managing workspace users
* [Webhook Integration](/guides/webhook-integration) - Real-time event notifications to external systems

**External Resources:**

* [Support Portal](https://support.royalti.io) - Get help from our support team
* [Status Page](https://status.royalti.io) - Check system status and uptime

***

**Last Updated:** January 2025

**Version:** 1.0
