Skip to main content
POST /webhooks/velt HTTP/1.1
Content-Type: application/json

{
  "actionType": "newlyAdded",
  "commentAnnotation": { "annotationId": "ANNOTATION_ID", "metadata": { "documentId": "DOC_ID" } },
  "latestComment": { "commentId": 123, "from": { "userId": "USER_1" } },
  "fromUser": { "userId": "USER_1" },
  "documentMetadata": { "url": "https://app.example.com/doc/123" }
}
// Pseudocode: handle webhook, fetch content, send email
app.post('/webhooks/velt', async (req, res) => {
  const evt = req.body;
  const annotationId = evt?.commentAnnotation?.annotationId;
  const latestCommentId = evt?.latestComment?.commentId;

  // 1) Fetch self-hosted content
  const annotation = await db.commentAnnotations.get(annotationId);
  const latestComment = annotation?.comments?.[latestCommentId];

  // 2) Resolve recipients (e.g., mentioned users)
  const recipients = await resolveMentionedUsers(annotation, latestComment);

  // 3) Compose email
  const subject = `${evt.fromUser?.userId} mentioned you`;
  const body = latestComment?.commentText || '';
  const pageUrl = evt?.documentMetadata?.url;

  // 4) Send via your provider
  await emailClient.send({ to: recipients, subject, html: renderTemplate({ body, pageUrl }) });

  res.sendStatus(200);
});
  • This feature is currently in beta and is subject to change.
  • 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.
  • If you are using REST API to add or update comments, ensure that you set isCommentResolverUsed and isCommentTextAvailable fields in the request object. Learn more

Overview

Velt supports self-hosting your comments and related data:
  • Comments can be stored on your own infrastructure, with only necessary identifiers on Velt servers.
  • Velt Components automatically hydrate comment data in the frontend by fetching from your configured data provider.
  • This gives you full control over comment data while maintaining all Velt collaboration features.
  • This automatically also ensures that the in-app notifications content is not stored on Velt servers. The content is generated using the comments data in the frontend.
Email notifications via Velt’s SendGrid integration are not available when you self-host comment content. Since the content lives on your infrastructure, Velt cannot construct and send emails via the sendgrid integration. Instead, use Webhooks to receive events (e.g., mentions, replies), fetch the relevant comment/notification content from your database, and send emails from your own email provider.

How does it work?

  • When comments are created, updated, deleted or requested, the SDK uses your configured CommentAnnotationDataProvider to handle storage and retrieval
  • The data provider implements get, save, and delete methods to interact with your database
  • Velt handles the data mapping and realtime synchronization while delegating persistence of actual content to your infrastructure
  • The data provider works at the Comment Annotation (Thread) level not at the individual Comment (Message) level.
  • For write requests (save, delete), the operation is first performed on your database and only if we get a success response, the SDK will perform the operation on the Velt server. If the operation fails on your database, the SDK will not perform the operation on the Velt server.
  • 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 comments from your database. On error we will retry.

save

Save comments to your database. Return a success or error response. On error we will retry.
If you are using REST API to add or update comments, ensure that you set isCommentResolverUsed and isCommentTextAvailable fields in the request object. Learn more

delete

Delete comments from your database. Return a success or error response. On error we will retry.

config

Configuration for the comment data provider.

Example Implementation

  • React / Next.js
  • Other Frameworks
const fetchCommentsFromDB = async (request: GetCommentResolverRequest) => {
    // Fetch comment annotations from your DB
    const result = await __getCommentsFromYourDB__(request)
      .then((response) => {
        return { data: response, success: true, statusCode: 200 };
      })
      .catch((error) => {
        return { success: false, statusCode: 500 };
      });

    return result;
};

const saveCommentsToDB = async (request: SaveCommentResolverRequest) => {
    const result = await __saveCommentsToYourDB__(request)
      .then((response) => {
        return { success: true, statusCode: 200 };
      })
      .catch((error) => {
        return { success: false, statusCode: 500 };
    });
    return result;
};

const deleteCommentsFromDB = async (request: DeleteCommentResolverRequest) => {
    const result = await __deleteCommentsFromYourDB__(request)
      .then((response) => {
        return { success: true, statusCode: 200 };
      })
      .catch((error) => {
        return { success: false, statusCode: 500 };
    });
    return result;
};

const commentResolverConfig: ResolverConfig = {
    resolveTimeout: 2000,
    getRetryConfig: {
        retryCount: 3,
        retryDelay: 2000
    },
    saveRetryConfig: {
        retryCount: 3,
        retryDelay: 2000
    },
    deleteRetryConfig: {
        retryCount: 3,
        retryDelay: 2000
    }
};


const commentAnnotationDataProvider: CommentAnnotationDataProvider = {
    get: fetchCommentsFromDB,
    save: saveCommentsToDB,
    delete: deleteCommentsFromDB,
    config: commentResolverConfig
};

<VeltProvider 
    apiKey='YOUR_API_KEY'
    dataProviders={{
        comment: commentAnnotationDataProvider
    }}
>
</VeltProvider>

Triggering Email Notifications when Self-Hosting

When you self-host content, use Webhooks to trigger emails from your own system:
1

Enable relevant webhooks

Subscribe to comment-related events (e.g., user mentions, replies). See Webhooks basics.
2

Receive webhook and fetch content from your DB

Your server receives the webhook event. Use IDs from the payload (e.g., annotationId, commentId) to query your own comment and notification content from your database via your Data Provider.
3

Assemble email content and recipients

Combine the webhook event context with the self-hosted content to build the subject, body, and list of recipients (e.g., mentioned users).
4

Send email via your provider

Use your own email service (SendGrid under your account, SES, Postmark, etc.) to send the email.
If you previously configured SendGrid in Velt Console, that configuration will not be used for self-hosted content. Use your own SendGrid account or another email provider from your server.
POST /webhooks/velt HTTP/1.1
Content-Type: application/json

{
  "actionType": "newlyAdded",
  "commentAnnotation": { "annotationId": "ANNOTATION_ID", "metadata": { "documentId": "DOC_ID" } },
  "latestComment": { "commentId": 123, "from": { "userId": "USER_1" } },
  "fromUser": { "userId": "USER_1" },
  "documentMetadata": { "url": "https://app.example.com/doc/123" }
}
// Pseudocode: handle webhook, fetch content, send email
app.post('/webhooks/velt', async (req, res) => {
  const evt = req.body;
  const annotationId = evt?.commentAnnotation?.annotationId;
  const latestCommentId = evt?.latestComment?.commentId;

  // 1) Fetch self-hosted content
  const annotation = await db.commentAnnotations.get(annotationId);
  const latestComment = annotation?.comments?.[latestCommentId];

  // 2) Resolve recipients (e.g., mentioned users)
  const recipients = await resolveMentionedUsers(annotation, latestComment);

  // 3) Compose email
  const subject = `${evt.fromUser?.userId} mentioned you`;
  const body = latestComment?.commentText || '';
  const pageUrl = evt?.documentMetadata?.url;

  // 4) Send via your provider
  await emailClient.send({ to: recipients, subject, html: renderTemplate({ body, pageUrl }) });

  res.sendStatus(200);
});
You now send email notifications from your own infrastructure while keeping content self-hosted.

Sample Data

  • Stored on your database
  • Stored on Velt servers
{
    "ANNOTATION_ID": {
        "annotationId": "ANNOTATION_ID",
        "metadata": {
            "apiKey": "API_KEY",
            "documentId": "DOCUMENT_ID",
            "organizationId": "ORGANIZATION_ID",
            "folderId": "FOLDER_ID"
        },
        "comments": {
            "COMMENT_ID": {
                "commentId": COMMENT_ID_NUMBER,
                "commentHtml": "<p>Comment Text</p>",
                "commentText": "Comment Text",
                "from": {
                    "userId": "USER_ID"
                }
            }
        },
        "from": {
            "userId": "1.1"
        }
    }
}
I