Skip to main content
  • This is currently only compatible with setDocuments method.
  • Ensure that the data providers are set prior to calling 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 reactions and related data:
  • Reactions can be stored on your own infrastructure, with only necessary identifiers on Velt servers.
  • Velt Components automatically hydrate reaction data in the frontend by fetching from your configured data provider.
  • This gives you full control over reaction data while maintaining all Velt collaboration features.
  • This automatically also ensures that the in-app notifications content related to reactions is not stored on Velt servers. The content is generated using the reactions data in the frontend.

How does it work?

When users add or remove reactions:
  1. The SDK uses your configured ReactionAnnotationDataProvider to handle storage
  2. Your data provider implements three key methods:
    • get: Fetches reactions from your database
    • save: Stores reactions and returns success/error
    • delete: Removes reactions from your database
The process works as follows: When a reaction operation occurs:
  1. The SDK first attempts to save/delete the reaction on your database
  2. If successful:
    • The SDK updates Velt’s servers with minimal metadata
    • The PartialReactionAnnotation object is updated with the reaction details including emoji, user, and metadata
    • When the reaction is saved, this information is stored on your end
    • Velt servers only store necessary identifiers, not the actual reaction content
  3. If the operation fails, no changes are made to Velt’s servers and the operation is retried if you have configured retries.
You can configure retries, timeouts, etc. for the data provider. Here are the methods that you need to implement on the data provider:

get

Method to fetch reactions from your database. On error we will retry.
const fetchReactionsFromDB = async (request) => {
  const response = await fetch('/api/velt/reactions/get', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(request)
  });
  return await response.json();
};

const reactionDataProvider = {
  get: fetchReactionsFromDB,
};

<VeltProvider
  apiKey='YOUR_API_KEY'
  dataProviders={{ reaction: reactionDataProvider }}
>
</VeltProvider>

save

Save reactions to your database. Return a success or error response. On error we will retry.
const saveReactionsToDB = async (request) => {
  const response = await fetch('/api/velt/reactions/save', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(request)
  });
  return await response.json();
};

const reactionDataProvider = {
  save: saveReactionsToDB,
};

<VeltProvider
  apiKey='YOUR_API_KEY'
  dataProviders={{ reaction: reactionDataProvider }}
>
</VeltProvider>

delete

Delete reactions from your database. Return a success or error response. On error we will retry.
const deleteReactionsFromDB = async (request) => {
  const response = await fetch('/api/velt/reactions/delete', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(request)
  });
  return await response.json();
};

const reactionDataProvider = {
  delete: deleteReactionsFromDB,
};

<VeltProvider
  apiKey='YOUR_API_KEY'
  dataProviders={{ reaction: reactionDataProvider }}
>
</VeltProvider>

config

Configuration for the reaction data provider.
const reactionResolverConfig = {
  resolveTimeout: 2000,
  saveRetryConfig: { retryCount: 3, retryDelay: 2000 },
  deleteRetryConfig: { retryCount: 3, retryDelay: 2000 }
};

Example Implementation

const fetchReactionsFromDB = async (request) => {
  const response = await fetch('/api/velt/reactions/get', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(request)
  });
  return await response.json();
};

const saveReactionsToDB = async (request) => {
  const response = await fetch('/api/velt/reactions/save', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(request)
  });
  return await response.json();
};

const deleteReactionsFromDB = async (request) => {
  const response = await fetch('/api/velt/reactions/delete', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(request)
  });
  return await response.json();
};

const reactionResolverConfig = {
  resolveTimeout: 2000,
  saveRetryConfig: { retryCount: 3, retryDelay: 2000 },
  deleteRetryConfig: { retryCount: 3, retryDelay: 2000 }
};

const reactionDataProvider = {
  get: fetchReactionsFromDB,
  save: saveReactionsToDB,
  delete: deleteReactionsFromDB,
  config: reactionResolverConfig
};

<VeltProvider
  apiKey='YOUR_API_KEY'
  dataProviders={{ reaction: reactionDataProvider }}
>
</VeltProvider>

Sample Data

{
    "ANNOTATION_ID": {
        "annotationId": "ANNOTATION_ID",
        "metadata": {
            "apiKey": "API_KEY",
            "documentId": "DOCUMENT_ID",
            "organizationId": "ORGANIZATION_ID",
            "folderId": "FOLDER_ID"
        },
        "reactions": {
            "REACTION_ID": {
                "reactionId": "REACTION_ID",
                "emoji": "thumbsup",
                "from": {
                    "userId": "USER_ID"
                },
                "createdAt": 1752638419744
            }
        },
        "from": {
            "userId": "USER_ID"
        }
    }
}