Skip to content

SDK client

Use the core client from browser or server code. Configure access first; each method returns a promise.

Read feedback and releases

Follow installation and identity before calling the client. Public keys see only the feedback exposed by the public API; a customer token does not unlock internal items.

import { Reflet } from "reflet-sdk";

const reflet = new Reflet({ publicKey: "fb_pub_YOUR_PUBLIC_KEY" });
const page = await reflet.list({
  search: "filters",
  sortBy: "votes",
  limit: 20,
  offset: 0,
});

if (page.hasMore) {
  const nextPage = await reflet.list({
    search: "filters",
    sortBy: "votes",
    limit: 20,
    offset: 20,
  });
}

List options are search (title and description), status (open, under_review, planned, in_progress, completed, or closed), statusId (custom status), sortBy (votes, newest, oldest, or comments), limit (at most 100), and offset.

Submit and respond

All writes except creation need a server-signed user token. Passing user is not enough. See trusted identity.

import { Reflet } from "reflet-sdk";

export async function respondToFeedback(token: string, feedbackId: string) {
  const reflet = new Reflet({
    publicKey: "fb_pub_YOUR_PUBLIC_KEY",
    userToken: token, // Signed by your authenticated server.
  });

  const vote = await reflet.vote(feedbackId);
  const { commentId } = await reflet.comment({
    feedbackId,
    body: "This also happens when I return from a bookmarked item.",
  });
  await reflet.subscribe(feedbackId);
  return { vote, commentId };
}

Optional creation context can include page information, metadata, selected elements, and console warnings/errors. The core client does not collect these automatically. For a capture UI, use the floating feedback widget. Survey client methods are documented in surveys.

Attach a screenshot

Upload bytes to the one-shot storage URL, then attach the returned storage ID to an existing feedback item. saveScreenshot returns { screenshotId }. Upload and attachment are separate operations; a failed attachment does not mean feedback creation failed.

import { Reflet } from "reflet-sdk";

export async function attachScreenshot(
  reflet: Reflet,
  feedbackId: string,
  image: Blob,
) {
  const { uploadUrl } = await reflet.getScreenshotUploadUrl();
  const upload = await fetch(uploadUrl, {
    method: "POST",
    headers: { "Content-Type": image.type },
    body: image,
  });
  if (!upload.ok) {
    throw new Error(`Screenshot upload failed (${upload.status})`);
  }

  const result: unknown = await upload.json();
  if (
    typeof result !== "object" || result === null ||
    !("storageId" in result) || typeof result.storageId !== "string"
  ) {
    throw new Error("Screenshot upload returned no storageId");
  }

  return await reflet.saveScreenshot({
    feedbackId,
    storageId: result.storageId,
    mimeType: image.type,
    size: image.size,
    captureSource: "widget",
  });
}

Other attachment fields are filename, width, height, pageUrl, annotatedStorageId, and annotations. Use captureSource: "element" for a selected-element close-up. The published declarations include the annotation shape. Review and redact images before upload; direct client calls do not apply widget capture protections.

Handle errors

import { Reflet, RefletError } from "reflet-sdk";

const reflet = new Reflet({ publicKey: "fb_pub_YOUR_PUBLIC_KEY" });

try {
  await reflet.create({
    title: "Keep my filters",
    description: "Filters reset after opening an item.",
  });
} catch (error) {
  if (error instanceof RefletError) {
    // status 0 means the network request failed.
    // Use status to choose UI copy; message contains the API's explanation.
    throw new Error(`Feedback was not sent (${error.status}): ${error.message}`);
  }
  throw error;
}
  • RefletError exposes status, message, and optional code.
  • HTTP 401 becomes RefletAuthError; HTTP 404 becomes RefletNotFoundError. Other HTTP failures, including 400, are RefletError. Do not rely on the exported RefletValidationError for API validation failures.
  • Network failures have status 0. Invalid JSON also throws RefletError with the response status.
  • 403 can indicate an origin or access restriction. 429 can indicate a rate limit or write quota. Keep the returned message visible and consult the API guide.
  • The client has no automatic retry or request cache. Do not blindly repeat creation or comments after an uncertain network failure.