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

# Notifications

> Self-host your custom notification PII data while using Velt's notification components. Keep notification content on your infrastructure with only minimal identifiers stored on Velt servers.

> [!WARNING]
>
> * Only notifications where `notificationSource === 'custom'` are routed through the notification resolver.
> * Ensure that the data providers are set prior to calling the `identify` method.
> * The data provider methods must return the correct status code (e.g. `200` for success, `500` for errors) and a `success` boolean in the response object. This ensures proper error handling and retries.

# Overview

Velt supports self-hosting your custom notification PII data:

* Custom notification content (headline, body, source data) can be stored on your own infrastructure, with only necessary identifiers on Velt servers.
* Velt components automatically hydrate notification data in the frontend by fetching from your configured data provider.
* This gives you full control over notification PII while maintaining all Velt notification features.

# How does it work?

When custom notifications are fetched or deleted:

1. The SDK uses your configured [`NotificationDataProvider`](https://velt.dev/docs/api-reference/sdk/models/data-models#notificationdataprovider) to handle retrieval and deletion.
2. Your data provider implements two optional methods:
   * `get`: Fetches notification PII from your database
   * `delete`: Removes notification PII from your database

**Resolution pipeline order**: notification → user → comment

**The process works as follows:**

When a notification fetch operation occurs:

1. The SDK calls your `get` handler with the notification IDs and organization ID.
2. If successful:
   * The notification objects are hydrated with your returned PII fields.
   * The [`Notification`](https://velt.dev/docs/api-reference/sdk/models/data-models#notification) object is updated with `isNotificationResolverUsed: true` once enriched via the notification resolver.
3. If the operation fails, the notification is rendered without enriched PII, and the operation is retried if you have configured retries.

# Implementation Approaches

You can implement notification self-hosting using either of these approaches:

1. **Endpoint based**: Provide endpoint URLs and let the SDK handle HTTP requests
2. **Function based**: Implement `get` and `delete` methods yourself

Both approaches are fully backward compatible and can be used together.

| Feature | Function based | Endpoint based |
| - | - | - |
| **Best For** | Complex setups requiring middleware logic, dynamic headers, or transformation before sending | Standard REST APIs where you just need to pass the request "as-is" to the backend |
| **Implementation** | You write the `fetch()` or `axios` code | You provide the endpoint `url`, static or async `headers`, and optional `credentials` |
| **Flexibility** | High | Medium |
| **Speed** | Medium | High |

## Endpoint based DataProvider

Instead of implementing custom methods, you can configure endpoints directly and let the SDK handle HTTP requests.

> [!NOTE]
>
> `headers` may also be an async function resolved per request, and `credentials` (`'include' | 'same-origin' | 'omit'`) enables cookie/session auth — see [Async headers and credentials](https://velt.dev/docs/self-hosting/partial/overview.md#async-headers-and-credentials) for details.

### getConfig

Config-based endpoint for fetching notification PII. The SDK automatically makes HTTP POST requests with the request body.

* Type: [`ResolverEndpointConfig`](https://velt.dev/docs/api-reference/sdk/models/data-models#resolverendpointconfig)
* Request body format: [`GetNotificationResolverRequest`](https://velt.dev/docs/api-reference/sdk/models/data-models#getnotificationresolverrequest)
* Response format: [`ResolverResponse<Record<string, PartialNotification>>`](https://velt.dev/docs/api-reference/sdk/models/data-models#resolverresponse)

> [tab] React / Next.js
>
> ```jsx theme={null}
> const notificationDataProvider = {
>   config: {
>     getConfig: {
>       url: 'https://your-backend.com/api/velt/notifications/get',
>       headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
>     }
>   }
> };
>
> <VeltProvider
>   apiKey='YOUR_API_KEY'
>   dataProviders={{ notification: notificationDataProvider }}
> >
> </VeltProvider>
> ```

> [tab] Other Frameworks
>
> ```js theme={null}
> const notificationDataProvider = {
>   config: {
>     getConfig: {
>       url: 'https://your-backend.com/api/velt/notifications/get',
>       headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
>     }
>   }
> };
>
> Velt.setDataProviders({ notification: notificationDataProvider });
> ```

### deleteConfig

Config-based endpoint for deleting notification PII. The SDK automatically makes HTTP POST requests with the request body.

* Type: [`ResolverEndpointConfig`](https://velt.dev/docs/api-reference/sdk/models/data-models#resolverendpointconfig)
* Request body format: [`DeleteNotificationResolverRequest`](https://velt.dev/docs/api-reference/sdk/models/data-models#deletenotificationresolverrequest)
* Response format: [`ResolverResponse<undefined>`](https://velt.dev/docs/api-reference/sdk/models/data-models#resolverresponse)

> [tab] React / Next.js
>
> ```jsx theme={null}
> const notificationDataProvider = {
>   config: {
>     deleteConfig: {
>       url: 'https://your-backend.com/api/velt/notifications/delete',
>       headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
>     }
>   }
> };
>
> <VeltProvider
>   apiKey='YOUR_API_KEY'
>   dataProviders={{ notification: notificationDataProvider }}
> >
> </VeltProvider>
> ```

> [tab] Other Frameworks
>
> ```js theme={null}
> const notificationDataProvider = {
>   config: {
>     deleteConfig: {
>       url: 'https://your-backend.com/api/velt/notifications/delete',
>       headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
>     }
>   }
> };
>
> Velt.setDataProviders({ notification: notificationDataProvider });
> ```

### Endpoint based Complete Example

> [tab] React / Next.js
>
> ```jsx theme={null}
> const notificationDataProvider = {
>   config: {
>     getConfig: {
>       url: 'https://your-backend.com/api/velt/notifications/get',
>       headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
>     },
>     deleteConfig: {
>       url: 'https://your-backend.com/api/velt/notifications/delete',
>       headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
>     },
>     resolveTimeout: 5000,
>     getRetryConfig: { retryCount: 3, retryDelay: 2000 },
>     deleteRetryConfig: { retryCount: 3, retryDelay: 2000 }
>   },
> };
>
> <VeltProvider
>   apiKey='YOUR_API_KEY'
>   dataProviders={{ notification: notificationDataProvider }}
> >
> </VeltProvider>
> ```

> [tab] Other Frameworks
>
> ```js theme={null}
> const notificationDataProvider = {
>   config: {
>     getConfig: {
>       url: 'https://your-backend.com/api/velt/notifications/get',
>       headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
>     },
>     deleteConfig: {
>       url: 'https://your-backend.com/api/velt/notifications/delete',
>       headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
>     },
>     resolveTimeout: 5000,
>     getRetryConfig: { retryCount: 3, retryDelay: 2000 },
>     deleteRetryConfig: { retryCount: 3, retryDelay: 2000 }
>   },
> };
>
> Velt.setDataProviders({ notification: notificationDataProvider });
> ```

## Function based DataProvider

Implement custom methods to handle data operations yourself.

### get

Fetch notification PII data from your database. Called when notifications need to be hydrated in the frontend.

* Param: [`GetNotificationResolverRequest`](https://velt.dev/docs/api-reference/sdk/models/data-models#getnotificationresolverrequest)
* Return: [`Promise<ResolverResponse<Record<string, PartialNotification>>>`](https://velt.dev/docs/api-reference/sdk/models/data-models#resolverresponse)

> [tab] React / Next.js
>
> ```jsx theme={null}
> const getNotificationFromDB = async (request) => {
>   const response = await fetch('/api/velt/notifications/get', {
>     method: 'POST',
>     headers: { 'Content-Type': 'application/json' },
>     body: JSON.stringify(request)
>   });
>   return await response.json();
> };
>
> const notificationDataProvider = {
>   get: getNotificationFromDB,
> };
>
> <VeltProvider
>   apiKey='YOUR_API_KEY'
>   dataProviders={{ notification: notificationDataProvider }}
> >
> </VeltProvider>
> ```

> [tab] Other Frameworks
>
> ```js theme={null}
> const getNotificationFromDB = async (request) => {
>   const response = await fetch('/api/velt/notifications/get', {
>     method: 'POST',
>     headers: { 'Content-Type': 'application/json' },
>     body: JSON.stringify(request)
>   });
>   return await response.json();
> };
>
> const notificationDataProvider = {
>   get: getNotificationFromDB,
> };
>
> Velt.setDataProviders({ notification: notificationDataProvider });
> ```

### delete

Remove notification PII from your database. Called when a custom notification is deleted.

* Param: [`DeleteNotificationResolverRequest`](https://velt.dev/docs/api-reference/sdk/models/data-models#deletenotificationresolverrequest)
* Return: [`Promise<ResolverResponse<undefined>>`](https://velt.dev/docs/api-reference/sdk/models/data-models#resolverresponse)

> [tab] React / Next.js
>
> ```jsx theme={null}
> const deleteNotificationFromDB = async (request) => {
>   const response = await fetch('/api/velt/notifications/delete', {
>     method: 'POST',
>     headers: { 'Content-Type': 'application/json' },
>     body: JSON.stringify(request)
>   });
>   return await response.json();
> };
>
> const notificationDataProvider = {
>   delete: deleteNotificationFromDB,
> };
>
> <VeltProvider
>   apiKey='YOUR_API_KEY'
>   dataProviders={{ notification: notificationDataProvider }}
> >
> </VeltProvider>
> ```

> [tab] Other Frameworks
>
> ```js theme={null}
> const deleteNotificationFromDB = async (request) => {
>   const response = await fetch('/api/velt/notifications/delete', {
>     method: 'POST',
>     headers: { 'Content-Type': 'application/json' },
>     body: JSON.stringify(request)
>   });
>   return await response.json();
> };
>
> const notificationDataProvider = {
>   delete: deleteNotificationFromDB,
> };
>
> Velt.setDataProviders({ notification: notificationDataProvider });
> ```

### config

Configuration for the notification data provider.

* Type: [`NotificationResolverConfig`](https://velt.dev/docs/api-reference/sdk/models/data-models#notificationresolverconfig). Relevant properties:
  * `resolveTimeout`: Timeout duration (in milliseconds) for resolver operations
  * `getRetryConfig`: [`RetryConfig`](https://velt.dev/docs/api-reference/sdk/models/data-models#retryconfig). Configure retry behavior for get operations.
  * `deleteRetryConfig`: [`RetryConfig`](https://velt.dev/docs/api-reference/sdk/models/data-models#retryconfig). Configure retry behavior for delete operations.

```jsx theme={null}
const notificationResolverConfig = {
  resolveTimeout: 5000,
  getRetryConfig: { retryCount: 3, retryDelay: 2000 },
  deleteRetryConfig: { retryCount: 3, retryDelay: 2000 }
};
```

### Function based Complete Example

> [tab] React / Next.js
>
> ```jsx theme={null}
> const getNotificationFromDB = async (request) => {
>   const response = await fetch('/api/velt/notifications/get', {
>     method: 'POST',
>     headers: { 'Content-Type': 'application/json' },
>     body: JSON.stringify(request)
>   });
>   return await response.json();
> };
>
> const deleteNotificationFromDB = async (request) => {
>   const response = await fetch('/api/velt/notifications/delete', {
>     method: 'POST',
>     headers: { 'Content-Type': 'application/json' },
>     body: JSON.stringify(request)
>   });
>   return await response.json();
> };
>
> const notificationResolverConfig = {
>   resolveTimeout: 5000,
>   getRetryConfig: { retryCount: 3, retryDelay: 2000 },
>   deleteRetryConfig: { retryCount: 3, retryDelay: 2000 }
> };
>
> const notificationDataProvider = {
>   get: getNotificationFromDB,
>   delete: deleteNotificationFromDB,
>   config: notificationResolverConfig,
> };
>
> <VeltProvider
>   apiKey='YOUR_API_KEY'
>   dataProviders={{ notification: notificationDataProvider }}
> >
> </VeltProvider>
> ```

> [tab] Other Frameworks
>
> ```js theme={null}
> const getNotificationFromDB = async (request) => {
>   const response = await fetch('/api/velt/notifications/get', {
>     method: 'POST',
>     headers: { 'Content-Type': 'application/json' },
>     body: JSON.stringify(request)
>   });
>   return await response.json();
> };
>
> const deleteNotificationFromDB = async (request) => {
>   const response = await fetch('/api/velt/notifications/delete', {
>     method: 'POST',
>     headers: { 'Content-Type': 'application/json' },
>     body: JSON.stringify(request)
>   });
>   return await response.json();
> };
>
> const notificationResolverConfig = {
>   resolveTimeout: 5000,
>   getRetryConfig: { retryCount: 3, retryDelay: 2000 },
>   deleteRetryConfig: { retryCount: 3, retryDelay: 2000 }
> };
>
> const notificationDataProvider = {
>   get: getNotificationFromDB,
>   delete: deleteNotificationFromDB,
>   config: notificationResolverConfig,
> };
>
> Velt.setDataProviders({ notification: notificationDataProvider });
> ```

# Writing Resolver-Eligible Notifications

To create a notification whose content will be resolved from your own infrastructure at read time, set `isNotificationResolverUsed: true` and `notificationSource: 'custom'` in your REST write. Omit `displayHeadlineMessageTemplate` and `displayBodyMessage` — the resolver will supply that content when the notification is fetched by the client.

> [!NOTE]
>
> Only notifications where `notificationSource === 'custom'` are routed through the notification resolver. Notifications without this field will not call your data provider.

Minimal POST body for a resolver-eligible notification:

```JSON theme={null}
{
  "data": {
    "organizationId": "yourOrganizationId",
    "documentId": "yourDocumentId",
    "actionUser": {
      "userId": "yourUserId"
    },
    "notificationId": "custom-notif-001",
    "isNotificationResolverUsed": true,
    "notificationSource": "custom",
    "notifyUsers": [
      {
        "userId": "recipientUserId",
        "email": "recipient@example.com"
      }
    ],
    "notifyAll": false
  }
}
```

See the [Add Notifications API (v2)](https://velt.dev/docs/api-reference/rest-apis/v2/notifications/add-notifications.md) for the full parameter reference.

# Resolver Status Field

The [`Notification`](https://velt.dev/docs/api-reference/sdk/models/data-models#notification) object includes a field to detect whether the notification resolver was used:

| Field | Type | Description |
| - | - | - |
| `isNotificationResolverUsed` | `boolean` | `true` once the notification has been enriched via the notification resolver. Use this field to detect resolver usage downstream. |

# Sample Data

> [tab] Stored on your database
>
> ```json theme={null}
> {
>   "notificationId": "custom-notif-001",
>   "displayHeadlineMessageTemplate": "{actionUser} left a note on {document}",
>   "displayHeadlineMessageTemplateData": {
>     "actionUser": { "userId": "user-123", "name": "Alice" },
>     "recipientUser": { "userId": "user-456", "name": "Bob" },
>     "actionMessage": "Check this out"
>   },
>   "displayBodyMessage": "Hey Bob, take a look at this section.",
>   "notificationSourceData": {
>     "documentId": "doc-789",
>     "sectionId": "sec-2"
>   }
> }
> ```

> [tab] Stored on Velt servers
>
> ```json theme={null}
> {
>   "notificationId": "custom-notif-001",
>   "notificationSource": "custom",
>   "isNotificationResolverUsed": true,
>   "actionUser": { "userId": "user-123" },
>   "notifyUsers": { "a1b2c3": true },
>   "notifyUsersByUserId": { "f9e8d7": true }
> }
> ```
>
> Note: Headline/body templates, template data, and `notificationSourceData` are NOT stored on Velt servers when using the notification resolver. Only routing identifiers (`notificationId`, `actionUser`, `notifyUsers`, `notifyUsersByUserId`), `notificationSource`, and the `isNotificationResolverUsed` flag are stored.
>
> > [!NOTE]
> >
> > On Velt's DB, `notifyUsers` is a map keyed by **hashed email** (`{ [emailHash]: boolean }`) and `notifyUsersByUserId` is a map keyed by **hashed userId** (`{ [userIdHash]: boolean }`) — not the array-of-`{ userId, email }` shape you POST when *writing* a notification. The write-side shape (shown in [Writing Resolver-Eligible Notifications](#writing-resolver-eligible-notifications)) is the *input* Velt accepts; the stored-on-Velt shape is the hashed map.

# Debugging

You can subscribe to `dataProvider` events to monitor and debug notification resolver operations.

> [tab] React / Next.js
>
> ```jsx theme={null}
> import { useVeltClient } from '@veltdev/react';
>
> const { client } = useVeltClient();
>
> useEffect(() => {
>   if (!client) return;
>
>   const subscription = client.on('dataProvider').subscribe((event) => {
>     console.log('Data Provider Event:', event);
>   });
>
>   return () => subscription?.unsubscribe();
> }, [client]);
> ```

> [tab] Other Frameworks
>
> ```javascript theme={null}
> const subscription = Velt.on('dataProvider').subscribe((event) => {
>   console.log('Data Provider Event:', event);
> });
>
> // Unsubscribe when done
> subscription?.unsubscribe();
> ```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.