API Resources
AuditLogs
Comments
EmailTemplates
HashTags
Moderators
Notification Count
Notifications
Pages
PendingWebhookEvents
SSO Users
Subscriptions
Tenant Daily Usage
Tenants
Tenant Packages
Tenant Users
Users
Votes
Domain Configs
Question Configs
Question Results
Question Results Aggregation
The FastComments API
FastComments provides an API for interacting with many resources. Build integrations with our platform, or even build your own clients!
In this documentation, you will find all supported resources by the API documented with their request and response types.
For Enterprise customers, all API access is captured in the Audit Log.
Authentication
The API is authenticated by passing your api key as either an X-API-KEY
header or API_KEY
query parameter. You will also need your tenantId
for making API calls. This can be retrieved from the same page as your api key.
Security Note
These routes are meant to be called from a server. DO NOT call them from a browser. Doing so will expose your API key - this will provide full access to your account to anyone who can view the source code of a page!
Authentication Option One - Headers
- Header:
X-API-KEY
- Header:
X-TENANT-ID
Authentication Option Two - Query Parameters
- Query Param:
API_KEY
- Query Param:
tenantId
API Resources
Resource Usage
It should be noted that fetching data from the API is counted as usage on your account.
Each resource will list what that usage is in its own section.
Some resources cost more to serve than others. Each endpoint has a set cost of credits per API call. For some endpoints, the number of credits varies based on the options and response sizes.
API usage can be checked on the Billing Analytics page and is updated every few minutes.
Note!
We suggest reading the Pages documentation first, to help limit confusion when determining what values to pass for urlId
in the Comment API.
AuditLog Structure
An AuditLog
is an object that represents an audited event for tenants that have access to this feature.
The structure for the AuditLog object is as follows:
The audit log is immutable. It also cannot be written to manually. FastComments.com may only decide when to write to the audit log. However, you may read from it via this API.
Events in the audit log expire after two years.
GET /api/v1/audit-logs
This API uses pagination, provided by the skip
, before
, and after
parameters. AuditLogs are returned in pages of 1000
, ordered by when
and id
.
Fetching every 1000
logs has a credit cost of 10
.
By default, you will receive a list with the newest items first. This way, you can poll starting with skip=0
, paginating until you find the last record you've consumed.
Alternatively, you can sort oldest-first, and paginate until there are no more records.
Sorting can be done by setting order
to either ASC
or DESC
. The default is ASC
.
Querying by date is possible via before
and after
as timestamps with milliseconds. before
and after
are NOT inclusive.
Comment Structure
A Comment
object represents a comment left by a user.
The relationship between parent and child comments is defined via parentId
.
The structure for the Comment object is as follows:
Some of these fields are marked READONLY
- these are returned by the API but cannot be set.
Comment Text Structure
Comments are written in a FastComments flavor of markdown, which is just markdown plus traditional bbcode
style tags for images, like [img]path[/img]
.
Text is stored in two fields. The text the user entered is stored unmodified in the comment
field. This is rendered and stored in the commentHTML
field.
The allowed HTML tags are b, u, i, strike, pre, span, code, img, a, strong, ul, ol, li, and br
.
It's recommended to render the HTML, since it is a very small subset of HTML, building a renderer is pretty straightforward. There are multiple libraries for React Native and Flutter, for instance, to help with this
You may choose to render the un-normalized value of the comment
field. An example parser is here..
The example parser could also be adjusted to work with HTML, and transform the HTML tags into expected elements to render for your platform.
Tagging
When users are tagged in a comment, the information is stored in a list called mentions
. Each object in that list
has the following structure.
HashTags
When hashtags are used and successfully parsed, the information is stored in a list called hashTags
. Each object in that list
has the following structure. Hashtags can also be manually added to the comment hashTags
array for querying, if retain
is set.
GET /api/v1/comments
This API is used to get comments for displaying to a user. For example, it automatically filters out unapproved or spam comments.
Pagination
Pagination can be done in one of two ways, depending on performance requirements and use case:
- Fastest: Precalculated Pagination:
- This is how FastComments works when you use our prebuilt widgets and clients.
- Clicking "next" simply increases the page count.
- You can think of this as being retrieved by a key-value store.
- In this way, simply define a
page
parameter starting at0
and a sort direction asdirection
. - Page sizes can be customized via customization rules.
- Most Flexible: Flexible Pagination:
- In this way you can define custom
limit
andskip
parameters. Do not passpage
. - Sort
direction
is also supported. limit
is the total number to return afterskip
is applied.- Example: set
skip = 200, limit = 100
whenpage size = 100
andpage = 2
.
- Example: set
- Child comments still count in the pagination. You can get around this using the
asTree
option.- You can paginate children via
limitChildren
andskipChildren
. - You can limit the depth of the threads returned via
maxTreeDepth
.
- You can paginate children via
- In this way you can define custom
Threads
- When using
Precalculated Pagination
, comments are grouped by page and comments in threads affect the overall page.- In this way, threads can be determined on the client based on
parentId
. - For example, with a page with one top-level comment, and 29 replies, and setting
page=0
in the API - you will get just the top level comment and the 29 children. - Example image here illustrating multiple pages.
- In this way, threads can be determined on the client based on
- When using
Flexible Pagination
, you may define aparentId
parameter.- Set this to null to only get top-level comments.
- Then to view threads, call the API again and pass
parentId
. - A common solution is to make an API call for the top-level comments and then make parallel API calls to get comments for the children of each comment.
- NEW As of Feb 2023! Fetch as a tree using
&asTree=true
.- You can think of this as
Flexible Pagination as a Tree
. - Only the top-level comments count in the pagination.
- Set
parentId=null
to start the tree at the root (you must setparentId
). - Set
skip
andlimit
for pagination. - Set
asTree
totrue
. - The credits cost increases by
2x
, as our backend has to do much more work in this scenario. - Set
maxTreeDepth
,limitChildren
, andskipChildren
as desired.
- You can think of this as
Trees Explained
When using asTree
, it can be hard to reason about pagination. Here's a handy graphic:
Fetching Comments in The Context of a User
The /comments
API can be used in two contexts, for different use cases:
- For returning comments sorted and tagged with information for building your own client.
- In this case, define a
contextUserId
query parameter.
- In this case, define a
- For fetching comments from your backend for custom integrations.
- The platform will default to this without
contextUserId
.
- The platform will default to this without
Get Comments as a Tree
It's possible to get the comments returned as a tree, with pagination only counting the top-level comments.
Want to only get the top level comments and the immediate children? Here's one way:
However, in your UI you might need to know whether to show a "show replies" button on
each comment. When fetching comments via a tree there is a hasChildren
property tagged
onto comments when applicable.
Get Comments as a Tree, Searching by Hash Tag
It's possible to search by hashtag using the API, across your entire tenant (not limited to one page, or urlId
).
In this example, we omit urlId
, and we search by multiple hashtags. The API will only return comments that have all requested hashtags.
All Request Params
The Response
Helpful Tips
URL ID
You probably want to use the Comment
API with the urlId
parameter. You can call the Pages
API first, to see what the urlId
values available to you look like.
Anonymous Actions
For anonymous commenting you probably want to pass anonUserId
when fetching comments, and when performing flagging and blocking.
(!) This is required for many app stores as users must be able to flag user-created content they can see, even if they are not logged in. Not doing so may cause your app to be removed from said store.
Comments Not Being Returned
Check that your comments are approved, and are not spam.
GET /api/v1/comments/:id
This API provides the ability to fetch a single comment by id.
POST /api/v1/comments
This API endpoint provides the ability to create comments.
Common use cases are custom UIs, integrations, or imports.
Notes:
- This API can update the comment widget "live" if desired (this increases
creditsCost
from1
to2
). - This API will automatically create user objects in our system if email is provided.
- Trying to save two comments with different emails, but the same username, will result in an error for the second comment.
- If you are specifying
parentId
, and a child comment hasnotificationSentForParent
as false, we will send notifications for the parent comment. This is done every hour (we batch the notifications together to decrease the number of emails sent). - If you want to send welcome emails when creating users, or comment verification emails, set
sendEmails
totrue
in query parameters. - Comments created via this API will show up in the Analytics and Moderation pages of the admin app.
- "bad words" are still masked in the commenter names and comment text if the setting is turned on.
- Comments created via this API can still be checked for spam if desired.
- Configuration such as max comment length, if configured via the Customization Rule admin page, will apply here.
The minimum data required to submit that will show in the comment widget, is as follows:
A more realistic request may look like:
PATCH /api/v1/comments/:id
This API endpoint provides the ability to update a single comment.
Notes:
- This API can update the comment widget "live" if desired (this increases base
creditsCost
from1
to2
).- This can make migrating comments between pages "live" (changing
urlId
). - Migrations cost an additional
2
credits as pages are precalculated and this is CPU intensive.
- This can make migrating comments between pages "live" (changing
- Unlike the create API, this API will NOT automatically create user objects in our system if email is provided.
- Comments updated via this API can still be checked for spam if desired.
- Configuration such as max comment length, if configured via the Customization Rule admin page, will apply here.
- To allow users to update their comment text, you can just specify
comment
in the request body. We will generate the resultingcommentHTML
.- If you define both
comment
andcommentHTML
we will not automatically generate the HTML. - If the user adds mentions or hashtags in their new text, it will still be processed like the
POST
API.
- If you define both
- When updating
commenterEmail
on a comment, it is best to also specifyuserId
. Otherwise, you must ensure the user with this email belongs to your tenant, or the request will fail.
DELETE /api/v1/comments/:id
This API endpoint provides the ability to delete a comment.
Notes:
- This API can update the comment widget "live" if desired (this increases
creditsCost
from1
to2
). - This API will delete all child comments.
POST /api/v1/comments/:id/flag
This API endpoint provides the ability to flag a comment for a specific user.
Notes:
- This call must always be made in the context of a user. The user can be a FastComments.com User, SSO User, or Tenant User.
- If a flag-to-hide threshold is set, the comment will be automatically hidden live after it has been flagged the defined number of times.
- After it is automatically un-approved (hidden) - the comment can only be re-approved by an administrator or moderator. Un-flagging will not re-approve the comment.
For anonymous flagging, we must specify an anonUserId
. This can be an ID that represents the anonymous session, or a random UUID.
This allows us to support flagging and un-flagging comments even if a user is not logged in. This way, the comment can be marked as
flagged when comments are fetched with the same anonUserId
.
POST /api/v1/comments/:id/un-flag
This API endpoint provides the ability to un-flag a comment for a specific user.
Notes:
- This call must always be made in the context of a user. The user can be a FastComments.com User, SSO User, or Tenant User.
- After a comment is automatically un-approved (hidden) - the comment can only be re-approved by an administrator or moderator. Un-flagging will not re-approve the comment.
For anonymous flagging, we must specify an anonUserId
. This can be an ID that represents the anonymous session, or a random UUID.
POST /api/v1/comments/:id/block
This API endpoint provides the ability to block a user that wrote a given comment. It supports blocking from comments written by FastComments.com Users, SSO Users, and Tenant Users.
It supports a commentIdsToCheck
body parameter to check if any other potentially visible comments on the client should be blocked/unblocked after this action is performed.
Notes:
- This call must always be made in the context of a user. The user can be a FastComments.com User, SSO User, or Tenant User.
- The
userId
in the request is the user that is doing the blocking. For example:User A
wants to BlockUser B
. PassuserId=User A
and the comment id thatUser B
wrote. - Completely anonymous comments (no user id, no email) cannot be blocked and an error will be returned.
For anonymous blocking, we must specify an anonUserId
. This can be an ID that represents the anonymous session, or a random UUID.
This allows us to support blocking comments even if a user is not logged in by fetching the comments with the same anonUserId
.
POST /api/v1/comments/:id/un-block
This API endpoint provides the ability to un-block a user that wrote a given comment. It supports un-blocking from comments written by FastComments.com Users, SSO Users, and Tenant Users.
It supports a commentIdsToCheck
body parameter to check if any other potentially visible comments on the client should be blocked/unblocked after this action is performed.
Notes:
- This call must always be made in the context of a user. The user can be a FastComments.com User, SSO User, or Tenant User.
- The
userId
in the request is the user that is doing the un-blocking. For example:User A
wants to Un-BlockUser B
. PassuserId=User A
and the comment id thatUser B
wrote. - Completely anonymous comments (no user id, no email) cannot be blocked and an error will be returned.
Email Template Structure
An EmailTemplate
object represents configuration for a custom email template, for a tenant.
The system will select the email template to use via:
- Its type identifier, we call this
emailTemplateId
. These are constants. - The
domain
. We will first try to find a template for the domain that the related object (like aComment
) is tied to, and if a match is not found then we will try to find a template where domain is null or*
.
The structure for the EmailTemplate
object is as follows:
Notes
- You can get the valid
emailTemplateId
values from the/definitions
endpoint. - The
/definitions
endpoint also includes the default translations and test data. - Templates will fail to save with invalid structure or test data.
GET /api/v1/email-templates/:id
Individual EmailTemplates can be fetched by their corresponding id
(NOT emailTemplateId
).
GET /api/v1/email-templates
This API uses pagination, provided by the page
query parameter. EmailTemplates are returned in pages of 100
, ordered by createdAt
and then id
.
PATCH /api/v1/email-templates/:id
This API endpoint provides the ability to update an email template by only specifying the id and the attributes to update.
Note that all the same validations for creating a template also apply, for example:
- The template must render. This is checked with each update.
- You can't have duplicate templates for the same domain (otherwise one would be silently ignored).
POST /api/v1/email-templates
This API endpoint provides the ability to create email templates.
Notes:
- You can't have multiple templates with the same
emailTemplateId
with the same domain. - But you can have a wildcard template (
domain
=*
and a domain specific template for the sameemailTemplateId
). - Specifying
domain
is only relevant if you have different domains, or want to use specific templates for testing (domain
set tolocalhost
etc). - If you do specify
domain
it must match aDomainConfig
. On error a list of valid domains is provided. - The template syntax is EJS and is rendered with a 500ms timeout. P99 for rendering is <5ms, so if you hit 500ms something is wrong.
- Your template must render with your given
testData
to save. Render errors are aggregated and reported on in the dashboard (soon available via API).
The minimum data required to add a template is as follows:
You may want to have templates per-site, in which case you define domain
:
POST /api/v1/email-templates/render
This API endpoint provides the ability to preview email templates.
DELETE /api/v1/email-templates/:id
This route provides the removal of a single EmailTemplate
by id.
HashTag Structure
A HashTag
object represents a tag that can be left by a user. HashTags can be used to link to an external piece of content or to
tie related comments together.
The structure for the HashTag
object is as follows:
Notes:
- In some API endpoints you will see that the hashtag is used in the URL. Remember to URI-Encoded values. For example,
#
should instead be represented as%23
. - Some of these fields are marked
READONLY
- these are returned by the API but cannot be set.
GET /api/v1/hash-tags
This API uses pagination, provided by the page
query parameter. HashTags are returned in pages of 100
, ordered by tag
.
PATCH /api/v1/hash-tags/:tag
This route provides the ability to update a single HashTag
.
POST /api/v1/hash-tags
This route provides the ability to add a single HashTag
.
POST /api/v1/hash-tags/bulk
This route provides the ability to add up to 100 HashTag
objects at once.
DELETE /api/v1/hash-tags/:tag
This route provides the removal of a HashTag
user by the provided tag.
Note that unless automatic HashTag
creation is disabled, hashtags can be re-created by a user providing the hashtag when commenting.
Moderator Structure
A Moderator
object represents configuration for a moderator.
There are three types of moderators:
- Administrator users that have the
isCommentModeratorAdmin
flag. - SSO Users with the
isCommentModeratorAdmin
flag. - Regular commenters, or FastComments.com users, that are invited as Moderators.
The Moderator
structure is used to represent the Moderation State of use case 3
.
If you want to invite a user to be a moderator, via the API, use the Moderator
API by creating a Moderator
and inviting
them.
If the user does not have a FastComments.com account, the invite email will help them get setup. If they already have an account, they will
be given moderation access to your tenant and the Moderator
object's userId
will be updated to point to their user. You will not have API
access to their user, as in this case it belongs to themselves and managed by FastComments.com.
If you require complete management of the user's account, we recommend either using SSO, or adding them as a Tenant User and
then adding a Moderator
object to track their stats.
The Moderator
structure can be used as a stat tracking mechanism for use cases 1
and 2
. After creating the user, add a Moderator
object with their userId
defined and their stats will be tracked on the Comment Moderators Page.
The structure for the Moderator
object is as follows:
GET /api/v1/moderators/:id
This route returns a single moderator by their id.
GET /api/v1/moderators
This API uses pagination, provided by the skip
query parameter. Moderators are returned in pages of 100
, ordered by createdAt
and id
.
The cost is based on the number of moderators returned, costing 1 credit per 10
moderators returned.
PATCH /api/v1/moderators/:id
This API endpoint provides the ability to update a Moderator
by id
.
Updating a Moderator
has the following restrictions:
- The following values may not be provided when updating a
Moderator
:acceptedInvite
markReviewedCount
deletedCount
markedSpamCount
approvedCount
editedCount
bannedCount
verificationId
createdAt
- When a
userId
is specified, that user must exist. - When a
userId
is specified, they must belong to the sametenantId
specified in query params. - Two moderators in the same tenant cannot be added with the same
email
. - You may not change the
tenantId
associated with aModerator
.
POST /api/v1/moderators
This route provides the ability to add a single Moderator
.
Creating a Moderator
has the following restrictions:
- A
name
andemail
must always be provided. AuserId
is optional. - The following values may not be provided when creating a
Moderator
:acceptedInvite
markReviewedCount
deletedCount
markedSpamCount
approvedCount
editedCount
bannedCount
verificationId
createdAt
- When a
userId
is specified, that user must exist. - When a
userId
is specified, they must belong to the sametenantId
specified in query params. - Two moderators in the same tenant cannot be added with the same
email
.
We can create a Moderator
for a user which we only know the email:
Or we can create a Moderator
for a user which belongs to our tenant, to track their moderation stats:
POST /api/v1/moderators/:id/send-invite
This route provides the ability to invite a single Moderator
.
The following restrictions exist to send an invite email to a Moderator
:
- The
Moderator
must already exist. - The
fromName
may not be longer than100 characters
.
Notes:
- If a user with the provided email already exists, they will be invited to moderate your tenant's comments.
- If a user with the provided email does not exist the invite link will guide them through creating their account.
- The invite will expire after
30 days
.
We can create a Moderator
for a user which we only know the email:
This will send an email like Bob at TenantName is inviting you to be a moderator...
DELETE /api/v1/moderators/:id
This route provides the removal of a Moderator
by id.
Notification Count Structure
A NotificationCount
object represents the unread notification count and metadata for a user.
If there are no unread notifications, there will be no NotificationCount
for the user.
NotificationCount
objects are created automatically and cannot be created via the API. They also expire after one year.
You can clear a user's unread notification count by deleting their NotificationCount
.
The structure for the NotificationCount
object is as follows:
GET /api/v1/notification-count/:user_id
This route returns a single NotificationCount
by user id. With SSO, the user id is in the format <tenant id>:<user id>
.
If there are no unread notifications, there won't be a NotificationCount
- so you will get a 404.
This is different than notifications/count
in that it is much faster, but does not allow filtering.
DELETE /api/v1/notification-count/:user_id
This route deletes a single NotificationCount
by user id. With SSO, the user id is in the format <tenant id>:<user id>
.
This will clear the user's unread notification count (the red bell in the comment widget will fade out and the count will go away).
Notification Structure
A Notification
object represents a notification for a user.
Notification
objects are created automatically and cannot be created via the API. They also expire after one year.
Notifications cannot be deleted. They can however be updated to set viewed
to false
, and you can query by viewed
.
A user may also opt out of notifications for a specific comment by setting optedOut
in the notification to true
. You can opt in again by setting it to false
.
There are different notification types - check relatedObjectType
and type
.
The ways notifications are created is quite flexible and can be triggered by many scenarios (see NotificationType
).
As of today, the existence of a Notification
does not actually imply an email is or should be sent. Rather, the notifications
are used for the notification feed and related integrations.
The structure for the Notification
object is as follows:
GET /api/v1/notifications
This route returns up to 30 Notification
objects sorted by createdAt
, newest first.
You can filter by userId
. With SSO, the user id is in the format <tenant id>:<user id>
.
GET /api/v1/notifications/count
This route returns an object containing the number of notifications under a count
parameter.
It is slower than /notification-count/
and double the credits cost, but allows filtering on more dimensions.
You can filter by the same parameters as the /notifications
endpoint like userId
. With SSO, the user id is in the format <tenant id>:<user id>
.
PATCH /api/v1/notifications/:id
This API endpoint provides the ability to update a Notification
by id
.
Updating a Notification
has the following restrictions:
- You can only update the following fields:
viewed
optedOut
Page Structure
A Page
object represents the page that many comments may belong to. This relationship is defined by
urlId
.
A Page
stores information such as the page title, comment count, and urlId
.
The structure for the Page object is as follows:
GET /api/v1/pages
You can currently only fetch all pages (or a single page via /by-url-id
) associated with your account. If you'd like more fine-grained searching, reach out to us.
Helpful Tip
The Comment
API requires a urlId
. You can call the Pages
API first, to see what the urlId
values available to you
look like.
GET /api/v1/pages/by-url-id
Individual pages can be fetched by their corresponding urlId
. This can be useful for looking up page titles or comment counts.
Helpful Tip
Remember to URI Encode values like the urlId
.
PATCH /api/v1/pages/:id
This route provides the ability to update a single Page
.
Note
Some parameters in the Page object get automatically updated. These are the counts and title attributes. Counts cannot be updated
via the API since they are calculated values. The page title
can be set via the API, but would get overwritten if the comment widget is used on
a page with the same urlId
and a different page title.
POST /api/v1/pages
This API endpoint provides the ability to create pages.
A common use cases is access control.
Notes:
- If you've commented on a comment thread, or called the API to create a
Comment
, you've already created aPage
object! You can try fetching it via the/by-url-id
Page
route, passing in the sameurlId
passed to the comment widget. - The
Page
structure contains some calculated values. Currently, these arecommentCount
androotCommentCount
. They are populated automatically and cannot be set by the API. Attempting to do so will cause the API to return an error.
DELETE /api/v1/pages/:id
This route provides the removal of a single page by id.
Note that interacting with the comment widget for a page with the same urlId
will simply recreate the Page
seamlessly.
PendingWebhookEvent Structure
A PendingWebhookEvent
object represents a queued webhook event that is pending.
PendingWebhookEvent
objects are created automatically and cannot be manually created via the API. They also expire after one year.
They can be deleted which removes the task from the queue.
There are different event types - check eventType
(OutboundSyncEventType
) and type
(OutboundSyncType
).
A common use case for this API is to implement custom monitoring. You may want to call the /count
endpoint periodically
to poll the outstanding count for given filters.
The structure for the PendingWebhookEvent
object is as follows:
GET /api/v1/pending-webhook-events
This route returns a list of pending webhook events under a pendingWebhookEvents
parameter.
This API uses pagination, provided by the skip
parameter. PendingWebhookEvents are returned in pages of 100
, ordered by createdAt
newest first.
GET /api/v1/pending-webhook-events/count
This route returns an object containing the number of pending webhook events under a count
parameter.
You can filter by the same parameters as the /pending-webhook-events
endpoint
DELETE /api/v1/pending-webhook-events/:id
This route allows the deletion of a single PendingWebhookEvent
.
If you need to a bulk delete, call the GET API with pagination and then call this API sequentially.
SSOUser Structure
FastComments provides an easy to use SSO solution. Updating a user's information with the HMAC-based integration is as simple as having the user load the page with an updated payload.
However, it may be desirable to manage a user outside that flow, to improve consistency of your application.
The SSO User API provides a way to CRUD objects that we call SSOUsers. These objects are different from regular Users and kept separate for type safety.
The structure for the SSOUser object is as follows:
Access Control
Users can be broken into groups. This is what the groupIds
field is for, and is optional.
@Mentions
By default @mentions
will use username
to search for other sso users when the @
character is typed. If displayName
is used, then results matching
username
will be ignored when there is a match for displayName
, and the @mention
search results will use displayName
.
Subscriptions
With FastComments, users can subscribe to a page by clicking the bell icon in the comment widget and clicking Subscribe.
With a regular user, we send them notification emails based on their notification settings.
With SSO Users, we split this up for backwards compatibility. Users will only get sent these additional subscription notification
emails if you set optedInSubscriptionNotifications
to true
.
GET /api/v1/sso-users
This route returns SSO Users in pages of 100
. Pagination is provided by the skip
parameter. Users are sorted by their signUpDate
and id
.
GET /api/v1/sso-users/by-id/:id
This route returns a single SSO user by their id.
GET /api/v1/sso-users/by-email/:email
This route returns a single SSO user by their email.
PATCH /api/v1/sso-users/:id
This route provides the ability to update a single SSO user.
POST /api/v1/sso-users
This route provides the creation of a single SSO user.
Trying to create two users with the same ID will result in an error.
In this example we specify groupIds
for access control, but this is optional.
Integration Note
Data passed by the API can be overridden simply by passing a different SSO User HMAC payload. For example, if you set a username via the API, but then pass a different one via the SSO flow on page load, we will automatically update their username.
We will not update user parameters in this flow unless you explicitly specify them or set them to null (not undefined).
PUT /api/v1/sso-users/:id
This route provides the ability to update a single SSO user.
In this example we specify groupIds
for access control, but this is optional.
DELETE /api/v1/sso-users/:id
This route provides the removal of a single SSO user by their id.
Note that loading the comment widget again with a payload for this user will simply recreate the user seamlessly.
Deleting the user's comments is possible via the deleteComments
query parameter. Note that if this is true:
- All the user's comments will be deleted live.
- All child (now orphan) comments will be deleted or anonymized based on each comment's associated page configuration. For example if thread deletion mode is "anonymize", then replies will remain, and the user's comments will be anonymized. This only applies when
commentDeleteMode
isRemove
(the default value). - The
creditsCost
becomes2
.
Anonymized Comments
You can retain the user's comments but simply anonymize them by setting commentDeleteMode=1
.
If the user's comments are anonymized then the following values are set to null:
- commenterName
- commenterEmail
- avatarSrc
- userId
- anonUserId
- mentions
- badges
isDeleted
and isDeletedUser
is set to true
.
When rendering, the comment widget will use DELETED_USER_PLACEHOLDER
(default: "[deleted]") for the user's name and DELETED_CONTENT_PLACEHOLDER
for the comment. These can be customized via the Widget Customization UI.
Examples
Subscription Structure
A Subscription
object represents a subscription for a user.
Subscription
objects are created when a user clicks the notification bell in the comment widget and clicks "Subscribe to this page".
Subscriptions can also be created via the API.
Having a Subscription
object causes Notification
objects to be generated, and emails sent, when new comments are left on the root of the associated page
that the Subscription
is for. Sending of emails depends on the type of user. For regular users this depends on optedInNotifications
. For SSO Users this depends on optedInSubscriptionNotifications
. Note that some applications may not have the concept of a web-accessible page, in which case simply set urlId
to
the id of the item you are subscribing to (same value for urlId
you would pass to the comment widget).
The structure for the Subscription
object is as follows:
GET /api/v1/subscriptions/:id
This route returns up to 30 Subscription
objects sorted by createdAt
, newest first.
You can filter by userId
. With SSO, the user id is in the format <tenant id>:<user id>
.
POST /api/v1/subscriptions
This API endpoint provides the ability to create a Subscription
. Note that a user may only have one subscription per page, as more is redundant, and trying
to create more than one subscription for the same user for the same page will result in an error.
Creating a subscription will result in Notification
objects being created when a new comment is left on the root of the subscribed urlId
(when comment parentId
is null
).
DELETE /api/v1/subscriptions/:id
This route deletes a single Subscription
object by id.
TenantDailyUsage Structure
A TenantDailyUsage
object represents the usage for a tenant on a given day. If there was no activity for a given tenant on a given
day, that day will not have a TenantDailyUsage
object.
The TenantDailyUsage
object is not real time and may be minutes behind actual usage.
The structure for the TenantDailyUsage
object is as follows:
GET /api/v1/tenant-daily-usage
This route allows searching for the usage of a tenant by year, month, and day. Up to 365 objects can be returned, and the cost is 1 api credit per 10 objects.
Response objects are sorted by the date they are created (the oldest first).
Tenant Structure
The Tenant
defines a FastComments.com customer. They can be created via the API by tenants with white labeling access. White labeled tenants
cannot create other white labeled tenants (only one level of nesting is allowed).
The structure for the Tenant
object is as follows:
GET /api/v1/tenants/:id
This route returns a single Tenant by id.
GET /api/v1/tenants
This API returns tenants that are managed by your tenant.
Pagination is provided by the skip
query parameter. Tenants are returned in pages of 100
, ordered by signUpDate
, and id
.
The cost is based on the number of tenants returned, costing 1 credit per 10
tenants returned.
POST /api/v1/tenants
This route provides the ability to add a single Tenant
.
Creating a Tenant
has the following restrictions:
- A
name
is required. domainConfiguration
is required.- The following values may not be provided when creating a
Tenant
:hasFlexPricing
lastBillingIssueReminderDate
flexLastBilledAmount
- The
signUpDate
may not be in the future. - The
name
may not be longer than200 characters
. - The
email
may not be longer than300 characters
. - The
email
must be unique across all of FastComments.com tenants. - You may not create tenants if the parent tenant does not have a valid
TenantPackage
defined.- If your tenant was created via FastComments.com, this shouldn't be an issue.
- You may not create more tenants than defined under
maxWhiteLabeledTenants
in your package. - You must specify the
tenantId
query param which is the id of yourparent tenant
with white labeling enabled.
We can create a Tenant
with only a few parameters:
PATCH /api/v1/tenants/:id
This API endpoint provides the ability to update a Tenant
by id
.
Updating a Tenant
has the following restrictions:
- The following values may not be updated:
hasFlexPricing
lastBillingIssueReminderDate
flexLastBilledAmount
managedByTenantId
- The
signUpDate
may not be in the future. - The
name
may not be longer than200 characters
. - The
email
may not be longer than300 characters
. - The
email
must be unique across all of FastComments.com tenants. - When setting
billingInfoValid
totrue
,billingInfo
must be provided in the same request. - You may not update the
packageId
associated with your own tenant. - You may not update the
paymentFrequency
associated with your own tenant.
DELETE /api/v1/tenants/:id
This route provides the removal of a Tenant
and all associated data (users, comments, etc) by id.
The following restrictions exist around removing tenants:
- The tenant must be your own, or a white labeled tenant that you manage.
- The
sure
query parameter must be set totrue
.
Tenant Package Structure
The TenantPackage
defines package information available to a Tenant
. A tenant may have many packages available, but only
one in use at a given time.
A Tenant
cannot be used for any products until its packageId
points to a valid TenantPackage
.
There are two types of TenantPackage
objects:
- Fixed-pricing packages - where
hasFlexPricing
is false. - Flexible pricing - where
hasFlexPricing
is true.
In both case limits are defined on the account using the package, however with Flex the tenant is charged a base price plus
what they used, defined by the flex*
parameters.
A tenant may have multiple tenant packages and have the ability to change the package themselves from the Billing Info Page.
If you will be handling billing for tenants yourselves, you will still need to define a package for each tenant to define their limits. Simply set billingHandledExternally
to true
on the Tenant
and they
will not be able to change their billing information, or active package, themselves.
You may not create packages with higher limits than the parent tenant.
The structure for the TenantPackage
object is as follows:
GET /api/v1/tenant-packages/:id
This route returns a single Tenant Package by id.
GET /api/v1/tenant-packages
This API uses pagination, provided by the skip
query parameter. TenantPackages are returned in pages of 100
, ordered by createdAt
and id
.
The cost is based on the number of tenant packages returned, costing 1 credit per 10
tenant packages returned.
POST /api/v1/tenant-packages
This route provides the ability to add a single TenantPackage
.
Creating a TenantPackage
has the following restrictions:
- The following parameters are required:
name
tenantId
monthlyCostUSD
- Can be null.yearlyCostUSD
- Can be null.maxMonthlyPageLoads
maxMonthlyAPICredits
maxMonthlyComments
maxConcurrentUsers
maxTenantUsers
maxSSOUsers
maxModerators
maxDomains
hasDebranding
forWhoText
featureTaglines
hasFlexPricing
- If true, then allflex*
parameters are required.
- The
name
may not be longer than50 characters
. - Each
forWhoText
item may not be longer than200 characters
. - Each
featureTaglines
item may not be longer than100 characters
. - The
TenantPackage
must be "smaller" than the parent tenant. For example, all of themax*
parameters must have lower values than the parent tenant. - A white labeled tenant may have a maximum of five packages.
- Only tenants with white labeling access may create a
TenantPackage
. - You may not add packages to your own tenant. :)
We can create a TenantPackage
as follows:
PATCH /api/v1/tenant-packages/:id
This API endpoint provides the ability to update a TenantPackage
by id
.
Updating a TenantPackage
has the following restrictions:
- If you are setting
hasFlexPricing
to true, then allflex*
parameters are required in that same request. - The
name
may not be longer than50 characters
. - Each
forWhoText
item may not be longer than200 characters
. - Each
featureTaglines
item may not be longer than100 characters
. - The
TenantPackage
must be "smaller" than the parent tenant. For example, all of themax*
parameters must have lower values than the parent tenant. - You may not change the
tenantId
associated with aTenantPackage
.
DELETE /api/v1/tenant-packages/:id
This route provides the removal of a TenantPackage
by id.
You may not remove a TenantPackage
that is in use (a tenant's packageId
points to the package). Update the Tenant
first.
Tenant User Structure
The TenantUser
defines a User
which is managed by a specific tenant. Their account is in complete control of the tenant
they are associated with, and their account can be updated or deleted via the UI or API.
Tenant users can be administrators with all permissions and access to the Tenant
, or they can be limited to specific permissions to
moderate comments, access API keys, etc.
The structure for the TenantUser
object is as follows:
GET /api/v1/tenant-users/:id
This route returns a single TenantUser by id.
GET /api/v1/tenant-users
This API uses pagination, provided by the skip
query parameter. TenantUsers are returned in pages of 100
, ordered by signUpDate
, username
and id
.
The cost is based on the number of tenant users returned, costing 1 credit per 10
tenant users returned.
POST /api/v1/tenant-users
This route provides the ability to add a single TenantUser
.
Creating a TenantUser
has the following restrictions:
- A
username
is required. - A
email
is required. - The
signUpDate
may not be in the future. - The
locale
must be in the list of Supported Locales. - The
username
must be unique across all of FastComments.com. If this is an issue, we suggest using SSO instead. - The
email
must be unique across all of FastComments.com. If this is an issue, we suggest using SSO instead. - You may not create more tenant users than defined under
maxTenantUsers
in your package.
We can create a TenantUser
as follows
POST /api/v1/tenant-users/:id/send-login-link
This route provides the ability to send a login link to a single TenantUser
.
Useful when batch creating users and not having to instruct them on how to login to FastComments.com. This will just send them a "magic link" to login that
expires after 30 days
.
The following restrictions exist to send a login link to a TenantUser
:
- The
TenantUser
must already exist. - You must have access to manage the
Tenant
theTenantUser
belongs to.
We can send a login link to a TenantUser
as follows:
This will send an email like Bob at TenantName is inviting you to be a moderator...
PATCH /api/v1/tenant-users/:id
This route provides the ability to update a single TenantUser
.
Updating a TenantUser
has the following restrictions:
- The
signUpDate
may not be in the future. - The
locale
must be in the list of Supported Locales. - The
username
must be unique across all of FastComments.com. If this is an issue, we suggest using SSO instead. - The
email
must be unique across all of FastComments.com. If this is an issue, we suggest using SSO instead. - You cannot update the
tenantId
of a user.
We can create a TenantUser
as follows
DELETE /api/v1/tenant-users/:id
This route provides the removal of a TenantUser
by id.
Deleting the user's comments is possible via the deleteComments
query parameter. Note that if this is true:
- All the user's comments will be deleted live.
- All child (now orphan) comments will be deleted or anonymized based on each comment's associated page configuration. For example if thread deletion mode is "anonymize", then replies will remain, and the user's comments will be anonymized. This only applies when
commentDeleteMode
isRemove
(the default value). - The
creditsCost
becomes2
.
Anonymized Comments
You can retain the user's comments but simply anonymize them by setting commentDeleteMode=1
.
If the user's comments are anonymized then the following values are set to null:
- commenterName
- commenterEmail
- avatarSrc
- userId
- anonUserId
- mentions
- badges
isDeleted
and isDeletedUser
is set to true
.
When rendering, the comment widget will use DELETED_USER_PLACEHOLDER
(default: "[deleted]") for the user's name and DELETED_CONTENT_PLACEHOLDER
for the comment. These can be customized via the Widget Customization UI.
Examples
User Structure
User
is an object that represents a most-common denominator of all users.
Keep in mind that at FastComments we have a bunch of different use cases for users:
- Secure SSO
- Simple SSO
- Tenant Users (For example: Administrators)
- Commenters
This API is for Commenters and users created via Simple SSO. Basically, any user created
through your site can be accessed via this API. Tenant Users can also be fetched this way, but you'll get more information by interacting with the /tenant-users/
API.
For Secure SSO
please use the /sso-users/
API.
You cannot update these types of users. They created their account through your site, so we provide some basic read-only access, but
you cannot make changes. If you want to have this type of flow - you need to setup Secure SSO
.
The structure for the User
object is as follows:
GET /api/v1/users/:id
This route returns a single User by id.
Vote Structure
A Vote
object represents a vote left by a user.
The relationship between comments and vote is defined via commentId
.
The structure for the Vote object is as follows:
GET /api/v1/votes
Votes must be fetched by urlId
.
Types of Votes
There are three types of votes:
- Authenticated Votes, which are applied to the corresponding comment. You can create these via this API.
- Authenticated Votes, which are pending verification, and thus are not yet applied to the comment. These are created when a user uses the FastComments.com login to vote mechanism.
- Anonymous Votes, which are applied to the corresponding comment. These are created along with anonymous commenting.
These are returned in separate lists in the API to reduce confusion.
Anonymous Votes Notes
Note that anonymous votes created via this API will appear in the appliedAuthorizedVotes
list. They are considered authorized since they were created via the API with an API key.
The appliedAnonymousVotes
structure is for votes created without an email, API key, etc.
GET /api/v1/votes/for-user
Allows fetching votes left by a user on a given urlId
. Takes a userId
which can be any FastComments.com or SSO User
.
This is useful if you want to show if a user has voted on a comment. When fetching comments, simply call this API at the same time for the user with the
same urlId
.
If you're using anonymous voting then you'll want to pass anonUserId
instead.
Note that anonymous votes will appear in the appliedAuthorizedVotes
list. They are considered authorized since they were created via the API with an API key.
POST /api/v1/votes
This route provides the ability to add a single authorized Vote
. Votes can be up
(+1) or down
(-1).
Creating Anonymous Votes
Anonymous votes can be created by setting anonUserId
in the query params instead of userId
.
This id does not have to correspond to a user object anywhere (hence anonymous). It is simply an identifier for the session, so you can fetch votes again in the same session, to check if a comment has been voted.
If you do not have such a thing as "anonymous sessions" like FastComments does - you can simply set this to a random ID, like a UUID (although we appreciate smaller identifiers to save space).
Other Notes
- This API obeys tenant-level settings. For example, if you disable voting for a given page, and you attempt to create a vote via the API, it will fail with error code
voting-disabled
. - This API is live by default.
- This API will update the
votes
of the correspondingComment
.
DELETE /api/v1/votes/:id
This route provides the ability to delete a single Vote
.
Notes:
- This API obeys tenant-level settings. For example, if you disable voting for a given page, and you attempt to create a vote via the API, it will fail with error code
voting-disabled
. - This API is live by default.
- This API will update the
votes
of the correspondingComment
.
DomainConfig Structure
A DomainConfig
object represents configuration for a domain for a tenant.
The structure for the DomainConfig
object is as follows:
For Authentication
Domain Configuration is used to determine which sites can host the FastComments widget for your account. This is a basic form of authentication, meaning adding or removing any Domain Configurations can impact the availability of your FastComments installation in production.
Don't remove or update the domain
property of a Domain Config
for a domain that is currently in use unless disabling that domain is intended.
This has the same behavior as removing a domain from /auth/my-account/configure-domains.
Also note that removing a domain from the My Domains
UI will remove any corresponding configuration for that domain that may have been added via this UI.
For Email Customization
The unsubscribe link in the email footer, and the one-click-unsubscribe feature offered by many email clients, can be configured via this API by defining footerUnsubscribeURL
and emailHeaders
, respectively.
For DKIM
After defining your DKIM DNS records, simply update the DomainConfig with your DKIM configuration using the defined structure.
GET /api/v1/domain-configs
This API provides the ability to fetch all DomainConfig
objects for a tenant.
GET /api/v1/domain-configs/:domain
Individual DomainConfigs can be fetched by their corresponding domain
.
POST /api/v1/domain-configs
This API endpoint provides the ability to create domain configurations.
Adding configuration for a domain authorizes that domain for the FastComments account.
Common use cases of this API are initial setup, if many domains are desired to be added, or custom configuration for sending emails.
PATCH /api/v1/domain-configs/:domain
This API endpoint provides the ability to update a domain configuration by only specifying the domain and the attribute to update.
PUT /api/v1/domain-configs/:domain
This API endpoint provides the ability to replace a domain configuration.
DELETE /api/v1/domain-configs/:domain
This route provides the removal of a single DomainConfig
by id.
- Note: Removing a
DomainConfig
will un-authorize that domain from using FastComments. - Note: Re-adding a domain via the UI will recreate the object (with just
domain
populated).
QuestionConfig Structure
FastComments provides a way to construct questions and aggregate their results. An example of a question (hereafter called QuestionConfig
)
could be a star rating, a slider, or an NPS question (determined via type
).
Question data can be aggregated individually, together, over time, overall, by page, and so on.
The framework has all the capabilities needed to build client-side widgets (with your server in front of this API), admin dashboards, and reporting tools.
First, we have to define a QuestionConfig
. The structure is as follows:
GET /api/v1/question-configs
This route returns up to 100 QuestionConfig
objects at a time, paginated. The cost is 1 per every 100 objects. They are
sorted by question text ascending (question
field).
GET /api/v1/question-configs/:id
This route returns a single QuestionConfig
by its id.
POST /api/v1/question-configs
This API endpoint provides the ability to create a QuestionConfig
.
PATCH /api/v1/question-configs/:id
This route provides the ability to update a single QuestionConfig
.
The following structure represents all values that can be changed:
DELETE /api/v1/question-configs/:id
This route provides the removal of a QuestionConfig
by id.
This will delete all corresponding question results (but not the comments). This is part of the high credit cost.
QuestionResult Structure
In order to save results for questions, you create a QuestionResult
. You can then aggregate question results, and also
tie them to comments for reporting purposes.
GET /api/v1/question-results
This route returns up to 1000 QuestionResults
objects at a time, paginated. The cost is 1 per every 100 objects. They are
sorted by createdAt
, ascending. You can filter by various parameters.
GET /api/v1/question-results/:id
This route returns a single QuestionResult
by its id.
POST /api/v1/question-results
This API endpoint provides the ability to create a QuestionResult
.
PATCH /api/v1/question-results/:id
This route provides the ability to update a single QuestionResult
.
The following structure represents all values that can be changed:
DELETE /api/v1/question-results/:id
This route provides the removal of a QuestionResult
by id.
GET /api/v1/question-results-aggregate
This is where aggregation of results happens.
The aggregation response structure is as follows:
Here are the query parameters available for aggregation:
Here's an example request:
Example response:
Performance Notes
- For a cache miss aggregations generally take five seconds per million results.
- Otherwise, requests are constant-time.
Caching and Cost Notes
- When
forceRecalculate
is specified the cost is always10
, instead of the normal2
. - If the cache expires and data is recalculated, the cost is still a constant
2
ifforceRecalculate
is not specified. The cache expires based on the data set size aggregated (can vary between 30 seconds and 5 minutes). - This is to incentivize using the cache.
GET /api/v1/question-results-aggregate/combine/comments
This is where combination of results with comments happens. Useful for creating a "recent positive and negative comments" chart for a product, for example.
You can search via a range of values (inclusive), one or more questions, and by a starting date (inclusive).
The response structure is as follows:
Here are the query parameters available for aggregation:
Here's an example request:
Example response:
Caching and Cost Notes
- When
forceRecalculate
is specified the cost is always10
, instead of the normal2
. - If the cache expires and data is recalculated, the cost is still a constant
2
ifforceRecalculate
is not specified. - This is to incentivize using the cache.
In Conclusion
We hope you've found our API documentation thorough and easy to understand. If you find any gaps, let us know below.