Every Feedtack submission emits a FeedtackPayload — a versioned JSON object with enough context for developers or LLMs to act on the feedback without additional investigation.
Current schema version: 2.0.0
FeedtackPayload
interface FeedtackPayload {
schemaVersion: string; // "2.0.0"
id: string; // Unique ID, e.g. "ft_01j..."
timestamp: string; // ISO 8601 UTC
scope: "site" | "page" | "element";
submittedBy: FeedtackUser;
comment: string;
sentiment: "good" | "bad" | null;
pins: FeedtackPin[]; // Empty for site/page scope
page: FeedtackPageMeta;
viewport: FeedtackViewportMeta;
device: FeedtackDeviceMeta;
}Nested types
FeedtackPin
interface FeedtackPin {
index: number; // 1-based
color: string; // Hex color
x: number; // Document-relative X (px)
y: number; // Document-relative Y (px)
xPct: number; // X as % of document width
yPct: number; // Y as % of document height
target: FeedtackPinTarget;
}FeedtackPinTarget
interface FeedtackPinTarget {
selector: string; // CSS selector to the element
best_effort: boolean; // True if selector is unreliable
dataTestId: string | null;
elementPath: string | null;
tagName: string;
ancestors: AncestorNode[];
boundingRect: FeedtackBoundingRect;
}AncestorNode
interface AncestorNode {
tag: string;
id: string | null;
ariaLabel: string | null;
role: string | null;
type: string | null;
name: string | null;
title: string | null;
alt: string | null;
dataTestId: string | null;
dataFeedtackComponent: string | null;
nthChild: number | null;
nthOfType: number | null;
componentName: string | null;
}Page, Viewport, Device metadata
interface FeedtackPageMeta {
url: string; // Full URL
pathname: string; // Path only
title: string; // document.title
}
interface FeedtackViewportMeta {
width: number;
height: number;
scrollX: number;
scrollY: number;
devicePixelRatio: number;
}
interface FeedtackDeviceMeta {
userAgent: string;
platform: string;
touchEnabled: boolean;
}Lifecycle types
FeedbackItem
The full feedback record including replies, resolutions, and archives:
interface FeedbackItem {
payload: FeedtackPayload;
replies: FeedtackReply[];
resolutions: FeedtackResolution[];
archives: FeedtackArchive[];
}FeedtackReply
interface FeedtackReply {
id: string;
feedbackId: string;
author: FeedtackUser;
body: string;
timestamp: string; // ISO 8601 UTC
}FeedtackResolution / FeedtackArchive
interface FeedtackResolution {
feedbackId: string;
resolvedBy: FeedtackUser;
timestamp: string;
}
interface FeedtackArchive {
feedbackId: string;
archivedBy: FeedtackUser;
timestamp: string;
}FeedtackFilter
Used by loadFeedback to scope which items to load:
interface FeedtackFilter {
url?: string;
pathname?: string;
userId?: string;
scope?: FeedtackScope;
}Full payload example
{
"schemaVersion": "2.0.0",
"id": "ft_01j...",
"timestamp": "2026-04-09T13:42:00.000Z",
"submittedBy": { "id": "u1", "name": "Alice", "role": "designer" },
"scope": "element",
"comment": "This button doesn't do anything",
"sentiment": "bad",
"pins": [{
"index": 1,
"color": "#ef4444",
"x": 420, "y": 812,
"xPct": 29.2, "yPct": 78.4,
"target": {
"selector": "#submit-btn",
"best_effort": false,
"tagName": "BUTTON",
"dataTestId": "submit-btn",
"elementPath": "main > form#checkout > button#submit-btn",
"ancestors": [
{
"tag": "FORM", "id": "checkout",
"ariaLabel": null, "role": null,
"dataTestId": "checkout-form",
"componentName": "CheckoutForm",
"nthChild": null, "nthOfType": null
}
],
"boundingRect": { "x": 420, "y": 812, "width": 200, "height": 44 }
}
}],
"page": {
"url": "https://app.example.com/checkout",
"pathname": "/checkout",
"title": "Checkout"
},
"viewport": {
"width": 1440, "height": 900,
"scrollX": 0, "scrollY": 812,
"devicePixelRatio": 2
},
"device": {
"userAgent": "Mozilla/5.0...",
"platform": "MacIntel",
"touchEnabled": false
}
}