Documentation Index
Fetch the complete documentation index at: https://docs.velt.dev/llms.txt
Use this file to discover all available pages before exploring further.
- This is currently only compatible with the
setDocuments method.
- 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 success boolean in the response object. This ensures proper error handling and retries.
Overview
Velt supports self-hosting your activity log PII data:
- Activity content (comment text embedded in change history), feature-specific entity snapshots, and custom fields can be stored on your own infrastructure, with only necessary identifiers on Velt servers.
- Velt components automatically re-hydrate activity data in the frontend by fetching from your configured data provider.
- This gives you full control over PII while maintaining all Velt activity log features.
How does it work?
When activity records are created or read:
- The SDK uses your configured
ActivityAnnotationDataProvider to handle storage and retrieval.
- Your data provider implements two optional methods:
get: Fetches activity PII from your database and returns it for re-hydration
save: Stores PII fields stripped from the activity record before Velt writes to its backend
The process works as follows:
On write:
- The SDK strips configured PII fields from the activity record.
- Your
save handler receives the stripped data and stores it in your backend.
- Velt’s backend stores only the minimal identifiers.
On read:
- The SDK fetches the activity records from Velt’s backend (with PII absent).
- Your
get handler is called with the activity IDs.
- The SDK merges the returned PII back into the activity records before delivering them to your UI.
ActivityRecord.isActivityResolverUsed is set to true when PII was stripped by the resolver. Use this to show a loading state while re-hydration is pending.
Implementation
Function based DataProvider
Implement custom get and save methods to handle data operations yourself.
get
Fetch activity PII from your database. Called when activity records need to be re-hydrated in the frontend.
React / Next.js
Other Frameworks
const activityDataProvider = {
get: async (request) => {
const response = await fetch('/api/velt/activity/get', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(request),
});
return await response.json();
},
};
client.setDataProviders({ activity: activityDataProvider });
const activityDataProvider = {
get: async (request) => {
const response = await fetch('/api/velt/activity/get', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(request),
});
return await response.json();
},
};
Velt.setDataProviders({ activity: activityDataProvider });
save
Store activity PII fields stripped before Velt writes to its backend. Called when an activity record is created or updated.
React / Next.js
Other Frameworks
const activityDataProvider = {
save: async (request) => {
const response = await fetch('/api/velt/activity/save', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(request),
});
return await response.json();
},
};
client.setDataProviders({ activity: activityDataProvider });
const activityDataProvider = {
save: async (request) => {
const response = await fetch('/api/velt/activity/save', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(request),
});
return await response.json();
},
};
Velt.setDataProviders({ activity: activityDataProvider });
config
Configuration for the activity data provider.
- Type:
ResolverConfig. Relevant properties:
resolveTimeout: Timeout duration (in milliseconds) for resolver operations.
fieldsToRemove: string[]. Additional fields to strip from activity records before writing to Velt’s backend (e.g., ['customSensitiveField']).
const activityDataProvider = {
config: {
resolveTimeout: 60000,
fieldsToRemove: ['customSensitiveField'],
},
};
Complete Example
React / Next.js
Other Frameworks
// Using API methods
client.setDataProviders({
activity: {
get: async (request) => {
const response = await fetch('/api/velt/activity/get', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(request),
});
return await response.json();
},
save: async (request) => {
const response = await fetch('/api/velt/activity/save', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(request),
});
return await response.json();
},
config: {
resolveTimeout: 60000,
fieldsToRemove: ['customSensitiveField'],
},
},
});
Velt.setDataProviders({
activity: {
get: async (request) => {
const response = await fetch('/api/velt/activity/get', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(request),
});
return await response.json();
},
save: async (request) => {
const response = await fetch('/api/velt/activity/save', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(request),
});
return await response.json();
},
config: {
resolveTimeout: 60000,
fieldsToRemove: ['customSensitiveField'],
},
},
});
Loading State
Use isActivityResolverUsed on ActivityRecord to show a loading state while PII is being fetched from your backend:
const activities = useAllActivities();
activities?.map((activity) => (
activity.isActivityResolverUsed
? <ActivitySkeleton key={activity.id} />
: <ActivityItem key={activity.id} activity={activity} />
));