- 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:
- The SDK uses your configured
NotificationDataProvider to handle retrieval and deletion.
- 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:
- The SDK calls your
get handler with the notification IDs and organization ID.
- If successful:
- The notification objects are hydrated with your returned PII fields.
- The
Notification object is updated with isNotificationResolverUsed: true once enriched via the notification resolver.
- 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:
- Endpoint based: Provide endpoint URLs and let the SDK handle HTTP requests
- 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 url string and headers object |
| 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.
getConfig
Config-based endpoint for fetching notification PII. The SDK automatically makes HTTP POST requests with the request body.
React / Next.js
Other Frameworks
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>
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.
React / Next.js
Other Frameworks
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>
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
React / Next.js
Other Frameworks
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>
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.
React / Next.js
Other Frameworks
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>
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.
React / Next.js
Other Frameworks
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>
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. Relevant properties:
resolveTimeout: Timeout duration (in milliseconds) for resolver operations
getRetryConfig: RetryConfig. Configure retry behavior for get operations.
deleteRetryConfig: RetryConfig. Configure retry behavior for delete operations.
const notificationResolverConfig = {
resolveTimeout: 5000,
getRetryConfig: { retryCount: 3, retryDelay: 2000 },
deleteRetryConfig: { retryCount: 3, retryDelay: 2000 }
};
Function based Complete Example
React / Next.js
Other Frameworks
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>
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.
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:
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"actionUser": {
"userId": "yourUserId",
"name": "User Name",
"email": "user@example.com"
},
"notificationId": "custom-notif-001",
"isNotificationResolverUsed": true,
"notificationSource": "custom",
"notifyUsers": [
{
"userId": "recipientUserId",
"email": "recipient@example.com"
}
],
"notifyAll": false
}
}
See the Add Notifications API (v2) for the full parameter reference.
Resolver Status Field
The 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. |
Debugging
You can subscribe to dataProvider events to monitor and debug notification resolver operations.
React / Next.js
Other Frameworks
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]);
const subscription = Velt.on('dataProvider').subscribe((event) => {
console.log('Data Provider Event:', event);
});
// Unsubscribe when done
subscription?.unsubscribe();