FastComments.com

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

AuditLog Structure Internal Link

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:

AuditLog Structure
Copy Copy
1
2interface AuditLog {
3 id: string;
4 userId?: string;
5 username?: string;
6 resourceName: string;
7 crudType: 'c' | 'r' | 'u' | 'd' | 'login';
8 from: string;
9 url?: string;
10 ip?: string;
11 when: string;
12 description?: string;
13 serverStartDate: string;
14 objectDetails?: object;
15}
16

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.

Comment Structure Internal Link

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:

Comment Structure
Copy Copy
1
2interface Comment {
3 /** READONLY: Set to true if the spam engine determined the comment was spam. **/
4 aiDeterminedSpam?: boolean
5 /** Whether the comment is approved to show. Set to true when saving the comment, else it will be hidden. **/
6 approved?: boolean
7 /** The user's avatar. **/
8 avatarSrc?: string
9 /** Child comments. Not populated in all scenarios. Used when asTree is set to true via the API. **/
10 children: Comment[]
11 /** The commenter's raw comment. **/
12 comment: string
13 /** READONLY: The commenter's comment parsed into HTML. **/
14 commentHTML?: string
15 /** The commenter's email. Required if anonymous commenting is off. **/
16 commenterEmail?: string
17 /** The commenter's link (for example, their blog). **/
18 commenterLink?: string
19 /** The commenter's name. Always required. If not available, set to something like "Anonymous". **/
20 commenterName: string
21 /** The date the comment was left, in UTC epoch. **/
22 date: number
23 /** The "display label" for the comment - for example "Admin", "Moderator", or something like "VIP User". **/
24 displayLabel?: string
25 /** The domain the comment was posted on. **/
26 domain?: string
27 /** READONLY: The number of times the comment was flagged. **/
28 flagCount?: number
29 /** The #hashtags written in the comment that were successfully parsed. You can also manually add hashtags, for querying, but they won't display in the comment text automatically. **/
30 hashTags?: CommentHashTag[]
31 /** READONLY: Does the comment contain images? **/
32 hasImages?: boolean
33 /** READONLY: Does the comment contain links? **/
34 hasLinks?: boolean
35 /** READONLY: The unique comment id. **/
36 id: string
37 /** READONLY: Did the current user block the user that wrote this comment? **/
38 isBlocked?: boolean
39 /** READONLY: Is the comment by an admin? Automatically set based on userId. **/
40 isByAdmin?: boolean
41 /** READONLY: Is the comment by a moderator? Automatically set based on userId. **/
42 isByModerator?: boolean
43 /** Set to true if the comment was soft deleted (placeholder had to be left due to some other configuration). **/
44 isDeleted?: boolean
45 /** Set to true if the user's account was deleted and the comment had to be retained. **/
46 isDeletedUser?: boolean
47 /** READONLY: Is the flagged by the currently logged-in user (contextUserId)? **/
48 isFlagged?: boolean
49 /** Is the comment pinned? **/
50 isPinned?: boolean
51 /** Is the comment locked for new replies (moderators still can reply)? **/
52 isLocked?: boolean
53 /** Is the comment spam? **/
54 isSpam?: boolean
55 /** READONLY: Is the comment voted down for the current user (contextUserId)? **/
56 isVotedDown?: boolean
57 /** READONLY: Is the comment voted up for the current user (contextUserId)? **/
58 isVotedUp?: boolean
59 /** The locale the comment is in. If not provided, will be derived from the language accept HTTP header. **/
60 locale?: 'de_de' | 'en_us' | 'es_es' | 'fr_fr' | 'it_it' | 'ja_jp' | 'ko_kr' | 'pl_pl' | 'pt_br' | 'ru_ru' | 'tr_tr' | 'zh_cn' | 'zh_tw'
61 /** READONLY: The @mentions written in the comment that were successfully parsed. **/
62 mentions?: CommentUserMention[]
63 /** Optional metadata associated with the comment. **/
64 meta?: Record<string, string | number | boolean>
65 /** The optional list of moderation group ids associated with this comment. **/
66 moderationGroupIds?: string[]|null
67 /** READONLY: The id of the vote object that corresponds to the vote from the current user (contextUserId) on this comment. **/
68 myVoteId?: string
69 /** Whether notifications were sent for this comment for commenters. To prevent notifications being sent on imports, set this to true. **/
70 notificationSentForParent?: boolean
71 /** Whether notifications were sent for this comment for tenant users. To prevent notifications being sent on imports, set this to true. **/
72 notificationSentForParentTenant?: boolean
73 /** The title of the page this comment was on. **/
74 pageTitle?: string
75 /** If we're replying to a comment, this is the ID that we are replying to. **/
76 parentId?: string|null
77 /** Whether the comment is marked reviewed. **/
78 reviewed: boolean
79 /** The tenant id where the comment belongs. **/
80 tenantId: string
81 /** The user that wrote the comment. Created automatically when saving a comment with a name/email. **/
82 userId?: string|null
83 /** The URL to the location that this comment is visible, like a blog post. **/
84 url: string
85 /** A "cleaned" version of the urlId you passed us. When saving, you specify this field, but when you fetch the comment back this will be "cleaned" and your original value moved to "urlIdRaw". **/
86 urlId: string
87 /** READONLY: The original urlId you passed us. **/
88 urlIdRaw?: string
89 /** Is the user and this comment verified? **/
90 verified: boolean
91 /** Number of votes up. **/
92 votesUp?: number
93 /** Number of votes down. **/
94 votesDown?: number
95 /** The "karma" of the comment (= votes up - votes down). **/
96 votes?: number
97}
98

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.

The Comment Mentions Object
Copy CopyRun External Link
1
2interface CommentUserMention {
3 /** The user id. For SSO users, this will have your tenant id prefixed. **/
4 id: string
5 /** The final @mention tag text, including the @ symbol. **/
6 tag: string
7 /** The original @mention tag text, including the @ symbol. **/
8 rawTag: string
9 /** What type of user was tagged. user = FastComments.com account. sso = SSOUser. **/
10 type: 'user'|'sso'
11 /** If the user opts out of notifications, this will still be set to true. **/
12 sent: boolean
13}
14

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.

The Comment HashTag Object
Copy CopyRun External Link
1
2interface CommentHashTag {
3 /** The hashtag id. **/
4 id: string
5 /** The final #hashtag tag text, including the # symbol. **/
6 tag: string
7 /** If the hashtag is associated with a custom URL, this will be defined. **/
8 url?: string
9 /** If we should retain the hashtag, even if it does not exist in the comment text, when the comment is updated. Useful for tagging comments without changing comment text. **/
10 retain?: boolean
11}
12

Email Template Structure Internal Link

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 a Comment) 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:

Email Template Structure
Copy Copy
1
2interface EmailTemplate {
3 id: string
4 tenantId: string
5 emailTemplateId: string
6 displayName: string
7 /** READONLY **/
8 createdAt: string
9 /** READONLY **/
10 updatedAt: string
11 /** READONLY **/
12 updatedByUserId: string
13 /** The domain the template should be associated with. **/
14 domain?: string | '*' | null
15 /** The email template content in EJS syntax. **/
16 ejs: string
17 /** A map of overridden translation keys to values, for each supported locale. **/
18 translationOverridesByLocale: Record<string, Record<string, string>>
19 /** An object that represents the render context of the template. **/
20 testData: object
21}
22

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.

HashTag Structure Internal Link

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:

HashTag Structure
Copy Copy
1
2interface HashTag {
3 /** Should start with the "#" or desired character. **/
4 tag: string
5 /** An optional URL that the hashtag can point to. Instead of filtering comments by hashtag, the UI will redirect to this upon click. **/
6 url?: string
7 /** READONLY **/
8 createdAt: string
9}
10

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.

Notification Count Structure Internal Link

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:

NotificationCount Structure
Copy Copy
1
2interface NotificationCount {
3 id: string // user id
4 count: number
5 createdAt: string // date string
6 expireAt: string // date string
7}
8

Notification Structure Internal Link

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:

Notification Structure
Copy Copy
1
2enum NotificationObjectType {
3 Comment = 0,
4 Profile = 1,
5 Tenant = 2
6}
7
8enum NotificationType {
9 /** If someone replied to you. **/
10 RepliedToMe = 0,
11 /** If someone replied anywhere in a thread (even children of children) of a thread you commented on. **/
12 RepliedTransientChild = 1,
13 /** If your comment was up-voted. **/
14 VotedMyComment = 2,
15 /** If a new comment is left on the root of a page you're subscribed to. **/
16 SubscriptionReplyRoot = 3,
17 /** If someone commented on your profile. **/
18 CommentedOnProfile = 4,
19 /** If you have a DM. **/
20 DirectMessage = 5,
21 /** TrialLimits is for tenant users only. **/
22 TrialLimits = 6,
23 /** If you were @mentioned. **/
24 Mentioned = 7
25}
26
27interface Notification {
28 id: string
29 tenantId: string
30 /** With SSO, the user id is in the format `<tenant id>:<user id>`. **/
31 userId?: string
32 /** When working with SSO, you only have to worry about `userId`. **/
33 anonUserId?: string
34 /** urlId is almost always defined. It is only optional for tenant-level notifications, which are infrequent. **/
35 urlId?: string
36 /** URL is cached for quick navigation to the source of the notification. **/
37 url?: string
38 /** Page Title is cached for quick reading of notification source. **/
39 pageTitle?: string
40 relatedObjectType: NotificationObjectType
41 /** For example, comment id. **/
42 relatedObjectId: string
43 viewed: boolean
44 createdAt: string // date string
45 type: NotificationType
46 fromCommentId?: string
47 fromVoteId?: string
48 /** fromUserName and fromUserAvatarSrc are cached here for quick displaying of the notification. They are updated when the user is updated. **/
49 fromUserName: string
50 fromUserId: string
51 fromUserAvatarSrc?: string
52 /** Set this to true to stop getting notifications for this object. **/
53 optedOut?: boolean
54}
55

Page Structure Internal Link

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:

Page Structure
Copy Copy
1
2interface Page {
3 id: string
4 urlId: string
5 url: string
6 title?: string
7 createdAt: string
8 commentCount: number
9 rootCommentCount: number
10 /** Setting this to null means all SSO users can see the page. An empty list means it is closed to all users. **/
11 accessibleByGroupIds?: string[] | null
12 /** Is this page closed for new comments? **/
13 isClosed?: boolean
14}
15

SSOUser Structure Internal Link

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:

SSOUser Structure
Copy Copy
1
2interface SSOUser {
3 id: string
4 username: string
5 email?: string
6 websiteUrl?: string
7 signUpDate: number
8 createdFromUrlId?: string
9 loginCount?: number
10 avatarSrc?: string
11 optedInNotifications?: boolean
12 optedInSubscriptionNotifications?: boolean
13 displayLabel?: string
14 displayName?: string
15 isAccountOwner?: boolean
16 isAdminAdmin?: boolean
17 isCommentModeratorAdmin?: boolean
18 /** If null, Access Control will not be applied to the user. If an empty list, this user will not be able to see any pages or @mention other users. **/
19 groupIds?: string[] | null
20 createdFromSimpleSSO?: boolean
21 /** Don't let other users see this user's activity, including comments, on their profile. Default is true to provide secure profiles by default. **/
22 isProfileActivityPrivate?: boolean
23 /** Don't let other users leave comments on the user's profile, or see existing profile comments. Default false. **/
24 isProfileCommentsPrivate?: boolean
25 /** Don't let other users send direct messages to this user. Default false. **/
26 isProfileDMDisabled?: boolean
27 karma?: number
28}
29

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.

Subscription Structure Internal Link

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:

Subscription Structure
Copy Copy
1
2interface Subscription {
3 id: string
4 tenantId: string
5 /** With SSO, the user id is in the format `<tenant id>:<user id>`. **/
6 userId: string
7 anonUserId?: string
8 urlId: string
9 url?: string
10 pageTitle?: string
11 createdAt: string // date string
12}
13

TenantDailyUsage Structure Internal Link

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:

TenantDailyUsage Structure
Copy Copy
1
2export interface TenantDailyUsage {
3 yearNumber: number
4 monthNumber: number
5 dayNumber: number
6 commentFetchCount?: number
7 commentCreateCount?: number
8 conversationCreateCount?: number
9 voteCount?: number
10 accountCreatedCount?: number
11 userMentionSearch?: number
12 hashTagSearch?: number
13 gifSearchTrending?: number
14 gifSearch?: number
15 apiCreditsUsed?: number
16 createdAt: string
17 billed: boolean
18 /** Ignored for billing. **/
19 ignored: boolean
20}
21

Tenant Structure Internal Link

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:

Tenant Structure
Copy Copy
1
2export enum SiteType {
3 Unknown = 0,
4 WordPress = 1
5}
6
7/** This can also be handled via the DomainConfig API. **/
8export interface TenantDomainConfig {
9 domain: string
10 emailFromName?: string
11 emailFromEmail?: string
12 createdAt?: string,
13 siteType?: FastCommentsSiteType, // you probably want Unknown
14 logoSrc?: string, // raw image path
15 logoSrc100px?: string, // resized for thumbnails
16 footerUnsubscribeURL?: string,
17 emailHeaders?: Record<string, string>,
18 disableUnsubscribeLinks?: boolean,
19 dkim?: {
20 domainName: string,
21 keySelector: string,
22 privateKey: string
23 }
24}
25
26export interface TenantBillingInfo {
27 name: string
28 address: string
29 city: string
30 state: string
31 zip: string
32 country: string
33}
34
35export enum TenantPaymentFrequency {
36 Monthly = 0,
37 Annually = 1
38}
39
40export interface Tenant {
41 id: string
42 name: string
43 email?: string
44 signUpDate: number; // number due to "legacy" reasons
45 packageId?: string | null
46 paymentFrequency?: TenantPaymentFrequency
47 billingInfoValid?: boolean
48 billingHandledExternally?: boolean
49 createdBy?: string
50 isSetup?: boolean
51 domainConfiguration: FastCommentsAPITenantDomainConfig[]
52 billingInfo?: FastCommentsAPITenantBillingInfo
53 stripeCustomerId?: string
54 stripeSubscriptionId?: string
55 stripePlanId?: string
56 enableProfanityFilter?: boolean
57 enableSpamFilter?: boolean
58 lastBillingIssueReminderDate?: string
59 removeUnverifiedComments?: boolean
60 unverifiedCommentsTTLms?: number
61 commentsRequireApproval?: boolean
62 autoApproveCommentOnVerification?: boolean
63 sendProfaneToSpam?: boolean
64 /** @readonly - Is calculated based on packageId. **/
65 hasFlexPricing?: boolean
66 /** @readonly **/
67 flexLastBilledAmount?: number
68 /** @readonly - Is calculated based on packageId. **/
69 hasAuditing?: boolean
70}
71

User Structure Internal Link

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:

User Structure
Copy Copy
1
2export interface User {
3 /** This is also the id used as userId on comment objects. **/
4 id: string
5 username: string
6 /** A link to the commenter's blog, for example. **/
7 websiteUrl?: string
8 email: string
9 signUpDate: number
10 createdFromUrlId: string
11 createdFromTenantId: string
12 avatarSrc?: string
13 locale: FastCommentsLocale
14 displayLabel?: string
15 karma?: number
16}
17

Vote Structure Internal Link

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:

Vote Structure
Copy Copy
1
2interface Vote {
3 id: string
4 urlId: string
5 commentId: string
6 userId: string
7 direction: 1 | -1
8 createdAt: string
9}
10

QuestionConfig Structure Internal Link

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:

QuestionConfig Structure
Copy Copy
1
2type QuestionConfigType = 'nps' | 'slider' | 'star' | 'thumbs';
3
4interface QuestionConfig {
5 id: string
6 tenantId: string
7 name: string
8 question: string
9 helpText?: string
10 createdAt: string
11 createdBy: string
12 /** READONLY - incremented for each new response. **/
13 usedCount: number
14 /** A date string for when the configuration was last used (result left). **/
15 lastUsed?: string
16 type: QuestionConfigType
17 numStars?: number
18 min?: number
19 max?: number
20 defaultValue?: number
21 labelNegative?: string
22 labelPositive?: string
23 subQuestionIds?: string[]
24 alwaysShowSubQuestions?: boolean
25 reportingOrder: number
26}
27

QuestionResult Structure Internal Link

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.

QuestionResult Structure
Copy Copy
1
2interface QuestionResultMeta {
3 name: string
4 values: string[]
5}
6
7interface QuestionResult {
8 id: string
9 tenantId: string
10 urlId: string
11 anonUserId?: string
12 userId?: string
13 createdAt?: string
14 value: number
15 commentId?: string
16 questionId: string
17 meta?: QuestionResultMeta[]
18}
19

In Conclusion

We hope you've found our API documentation thorough and easy to understand. If you find any gaps, let us know below.