Replyke Documentation
Overview
Replyke is designed to simplify the development and implementation of social interaction features by offloading much of the backend work. This allows developers to focus on the frontend experience and reduces complexity. Unlike traditional architectures where all interactions pass through a central server, Replyke communicates directly with the client. You can regard Replyke as an oppnionated server-as-a-service.
This approach reduces complexity for developers but introduces unique challenges in ensuring that data remains valid across various custom use cases—especially when dealing with free-form metadata and content validation.
Replyke addresses many common validation needs out of the box, such as enforcing data ownership, managing user authorization, and implementing logical constraints like limiting users to a single vote per entity/comment. However, for application-specific validation—particularly for custom metadata or complex entity rules—developers need a way to extend Replyke’s built-in capabilities, so they can verify their own unique data structures.
To solve this, Replyke leverages a webhook-based system. Developers can define custom validation logic by exposing a webhook on their server, which will be triggered during certain events that migh require extra validation. Those events are: Entity creation or update, and User creation or update. This ensures that all relevant data is validated on the developer’s server before the operation is finalized in Replyke. The webhook response determines whether the operation proceeds or is rejected by Replyke, allowing for fine-grained control over data integrity.
By default, no webhooks are configured for new projects, meaning that no further validation takes place beyond Replyke’s built-in validation. While this might be sufficient for development, exposing a webhook is highly recommended for applications in production that require enhanced security and data validation to maintain the integrity of their data.
Webhooks for Validation
In Replyke’s dashboard developers can set up their webhook under settings. A single webhook is enough, and each payload includes a “type” field to help developers know which event the payload is associated with, and how to handle it. A single shared secret from the Replyke dashboard is sufficient for all webhooks. However, it is recommended to periodically rotate the secret for enhanced security.
For reference, the setup for validation webhooks is similar to the notifications webhook integration described in App Notifications Webhook Integration, with the added requirement of sending a signed response.
Falure to sd a respons would lead to Replyke rejecting the operation entirely.
Validation Webhook Endpoints
-
User Created This webhook validates user details before a user is created. The payload includes the following fields:
Field Description projectId
Always included data
Object containing the user details: - foreignId
: If integrating Replyke with an external user system, a foreign ID will be provided.- role
: Always “visitor” for new users- email
: Optional, user’s email address- name
: Optional, user’s name- username
: Optional, username- avatar
: Optional, URL to the avatar image- bio
: Optional, user biography- location
: Optional, user’s location- birthdate
: Optional, user’s birthdate- metadata
: Optional, free-form metadata- secureMetadata
: Optional, sensitive data -
User Updated This webhook validates user details before a user is updated. The payload structure is similar to the one used for user creation but includes only the updated fields within the
data
object. -
Entity Created This webhook validates entity details before an entity is created. The payload includes the following fields:
Field Description projectId
Always included data
Object containing entity details: - foreignId
: If entity data is attached to a resource from an external data set, a foreign ID will be provided.- sourceId
: Entities could be grouped by source ID to separate entity lists.- userId
: Optional, creator’s ID- title
: Optional, title of the entity- content
: Optional, content of the entity- attachments
: Optional, file attachments- mentions
: Optional, array of user mentions- keywords
: Optional, keywords for the entity- location
: Optional, geographic location- metadata
: Optional, free-form metadatainitiatorId
Optional, ID of the user initiating the action. Some apps don’t associate entties with the users who created them, which requires userId to remain null. For suce projects, this field is useful in the validation stage. -
Entity Updated This webhook validates entity details before an entity is updated. The payload structure is similar to the one used for entity creation but includes only the updated fields within the
data
object.
HMAC Signature and Security
To ensure secure communication between Replyke and your server, each webhook request includes an HMAC signature. This signature verifies the authenticity of the request and prevents tampering. The HMAC signature is calculated using the shared secret and the payload.
Supporting Functions
Here are supporting functions for HMAC validation and response signing:
import crypto from "crypto";
import { Request, Response } from "express";
/**
* Validate HMAC signature of an incoming request.
*/
export function validateIncomingHmac(req: Request, secret: string): void {
const signature = req.headers["x-signature"] as string;
const timestamp = req.headers["x-timestamp"] as string;
if (!signature || !timestamp) {
throw new Error("Missing HMAC signature or timestamp in headers");
}
// Reject requests older than 5 minutes to prevent replay attacks
if (Date.now() - parseInt(timestamp, 10) > 5 * 60 * 1000) {
throw new Error("Request timestamp expired");
}
// Compute the expected signature
const payload = JSON.stringify(req.body);
const expectedSignature = crypto
.createHmac("sha256", secret)
.update(`${timestamp}.${payload}`)
.digest("hex");
if (signature !== expectedSignature) {
throw new Error("Invalid HMAC signature");
}
}
/**
* Generate an HMAC signature for the response payload.
*/
export function signResponsePayload(payload: any, secret: string): string {
return crypto
.createHmac("sha256", secret)
.update(JSON.stringify(payload))
.digest("hex");
}
/**
* Handles unexpected errors, logs them, signs the error response, and sends it.
*/
export function handleError(
res: Response,
error: any,
sharedSecret: string
): void {
console.error("Error processing request:", error.message || error);
const responsePayload = { valid: false, message: "Server error" };
const responseSignature = signResponsePayload(responsePayload, sharedSecret);
res
.status(500)
.header("X-Response-Signature", responseSignature)
.send(responsePayload);
}
Example Implementation
import { Request as ExReq, Response as ExRes } from "express";
import {
validateIncomingHmac,
signResponsePayload,
handleError,
} from "./utility-functions";
export default async (req: ExReq, res: ExRes) => {
const sharedSecret = process.env.REPLYKE_WEBHOOKS_SECRET;
try {
const { projectId, type, data } = req.body;
// Step 1: Validate the incoming HMAC signature
validateIncomingHmac(req, sharedSecret!);
// Step 2: Add your event-specific validation logic here
switch (type) {
case "user.created":
console.log("Project ID:", projectId, "User data:", data);
break;
case "user.updated":
console.log("Project ID:", projectId, "User update data:", data);
break;
case "entity.created":
console.log("Project ID:", projectId, "Entity data:", data);
break;
case "entity.updated":
console.log("Project ID:", projectId, "Entity update data:", data);
break;
case "notification.created":
console.log("Project ID:", projectId, "Notification data:", data);
break;
}
// Step 3: Send a valid response if the data is valid, or false if it is not valid. Include your secret
const responsePayload = { valid: true };
const responseSignature = signResponsePayload(
responsePayload,
sharedSecret!
);
res
.status(200)
.header("X-Response-Signature", responseSignature)
.send(responsePayload);
} catch (err) {
handleError(res, err, sharedSecret!);
}
};
This example demonstrate complete webhook implementations and supporting functions, ensuring secure and accurate handling of data. Developers using other languages should replicate the HMAC signature logic to maintain compatibility.