Suggestra

Overview

Suggestra has two integration surfaces: the embed widget for leaving feedback on live websites, and the Business REST API for server-side access to threads, comments, and exports.

Create an account to add websites and generate keys.

Embed widget

The Suggestra widget loads from your Suggestra instance and renders a floating action button plus an overlay for pinning feedback to elements on the page. Embed keys (mk_…) are safe to include in public HTML.

Quick start

  1. Create a website in the Suggestra dashboard and copy its embed key.
  2. Add the script snippet before </body> on pages you want to collect feedback from.
  3. Sign in to Suggestra on the same browser and visit the site — the widget appears for allowed reviewers.

Installation

Add this script tag to every page where feedback should be available. Replace the API key with your website's embed key from the dashboard.

html
<script src="https://suggestra.dev/embed.js" data-api-key="mk_your_embed_key" defer></script>
src
URL of the bundled embed script served by your Suggestra instance (https://suggestra.dev/embed.js).
data-api-key
Your website embed key (mk_…). Identifies which site the widget belongs to.
defer
Recommended. Loads the script after HTML parsing without blocking render.

The widget automatically uses the script's origin as the API base URL. Requests are only accepted from origins that match the website domain configured in your dashboard.

How it works

  • Bootstrap — On load, the widget calls GET /api/embed/bootstrap with the current page path to fetch website settings, auth state, and open threads for that page.
  • Authentication — Reviewers must be signed in to Suggestra in the same browser. The widget uses session cookies on cross-origin requests (credentials included).
  • Pinning — New feedback creates a thread anchored to a DOM element with selector, XPath, coordinates, and scroll position so pins stay accurate as layouts change.
  • Screenshots — On Pro and Business plans, the widget can attach a viewport screenshot when creating a pin (Pro+ feature).
  • Quotas — Free plans have a monthly feedback limit. The widget shows quota usage and an upgrade link when the limit is reached.
  • Widget position — Left or right FAB placement is configured per website in the dashboard.

Reviewers & permissions

Not every visitor sees the commenting UI. A user can leave feedback when they are either:

  • The subscription owner (account that owns the website), or
  • An invited reviewer added on the website's edit page in the dashboard.

Visitors who are not signed in see a sign-in prompt. Signed-in users without access see a permission message. Manage reviewers from Dashboard → Websites → Edit after you sign in.

Embed API reference

The widget uses this JSON API internally. Base URL: https://suggestra.dev/api/embed. All requests require the website embed key via the X-Suggestra-Api-Key header. Cross-origin requests must come from the website's allowed domain; CORS credentials are enabled.

GET /api/embed/bootstrap

Load website config, auth state, quota, and threads for the current page.

Query parameters

  • page_path optional — Current page path including query string, e.g. /about?ref=home

Response

json
{
  "website": { "id": 1, "name": "Acme", "domain": "acme.com", "widgetPosition": "right" },
  "authenticated": true,
  "canComment": true,
  "hasScreenshots": true,
  "feedbackQuota": { "used": 3, "limit": 50, "canCreateThread": true },
  "upgradeUrl": "https://…/pricing",
  "user": { "id": 1, "displayName": "Alex", "avatarUrl": "…" },
  "loginUrl": "https://…/login",
  "threads": [ … ]
}
GET /api/embed/me

Check whether the current browser session is signed in.

Response

json
{ "authenticated": true, "user": { … } }
POST /api/embed/threads

Create a new feedback thread (pin). Requires signed-in reviewer with comment permission.

Request body

json
{
  "pageUrl": "https://acme.com/pricing",
  "pagePath": "/pricing",
  "body": "Headline feels cramped on mobile",
  "domSelector": "h1.hero",
  "domXPath": "/html/body/main/h1",
  "domTextSnippet": "Simple pricing",
  "anchorX": 120.5,
  "anchorY": 48.0,
  "pageScrollX": 0,
  "pageScrollY": 240,
  "viewportWidth": 1280,
  "viewportHeight": 800,
  "screenshot": "data:image/jpeg;base64,…"
}

Response

json
{ "id": 42, "pinNumber": 3, "status": "open", … }
POST /api/embed/threads/{id}/comments

Add a reply to an existing thread.

Request body

json
{ "body": "Good catch — fixing in the next deploy." }
PATCH /api/embed/threads/{id}/status

Update thread status from the widget. Only resolved, reopened, and closed are allowed.

Request body

json
{ "status": "resolved" }
PATCH /api/embed/threads/{id}/anchor

Re-anchor a pin after DOM changes. Same coordinate fields as thread creation.

Request body

json
{ "anchorX": 130.0, "anchorY": 52.0, "domSelector": "h1.hero", … }
DELETE /api/embed/threads/{id}

Delete a thread and all its comments. Returns 204 No Content.

Note: The embed API is designed for the Suggestra widget, not third-party clients. For backend integrations, use the Business API.

Business API

The Business REST API gives you programmatic access to websites, feedback threads, and comments. Outbound webhooks let your systems react to feedback events in real time. Both features are available on the Business plan and use secret credentials that must never be exposed in client-side code.

Business plan

Authentication

Base URL: https://suggestra.dev/api/v1

Send your secret key on every request:

http
Authorization: Bearer sk_your_secret_key
  • Keys start with sk_ and belong to your subscription, not an individual website.
  • Embed keys (mk_…) are not accepted on Business API routes.
  • Generate or rotate keys from Dashboard → API after upgrading.

Filters

These query parameters apply to GET /threads and GET /export:

Parameter Description
website or website_id Numeric website ID. Must belong to your account.
status One of open, resolved, reopened, closed.
from Earliest thread creation date (ISO 8601 or YYYY-MM-DD).
to Latest thread creation date. Date-only values include the full day.

Endpoints

GET /api/v1/websites

List all websites on your account, including embed keys and widget settings.

Response

json
{
  "websites": [
    {
      "id": 1,
      "name": "Acme Marketing",
      "domain": "acme.com",
      "widgetPosition": "right",
      "embedApiKey": "mk_…"
    }
  ]
}

Example

bash
curl -s -H "Authorization: Bearer sk_…" \
  "https://suggestra.dev/api/v1/websites"
GET /api/v1/threads

List feedback threads with nested comments. Supports filter query parameters.

Response

json
{
  "threads": [ { "id": 42, "status": "open", "comments": [ … ], … } ],
  "meta": {
    "count": 12,
    "filters": { "websiteId": 1, "status": "open", "from": null, "to": null }
  }
}

Example

bash
curl -s -H "Authorization: Bearer sk_…" \
  "https://suggestra.dev/api/v1/threads?website=1&status=open&from=2026-01-01"
PATCH /api/v1/threads/{id}

Update a thread status. Same transition rules as the dashboard.

Request body

json
{ "status": "resolved" }

Response

json
{ "thread": { "id": 42, "status": "resolved", … } }
POST /api/v1/threads/{id}/comments

Add a comment to a thread. Author defaults to "API" when name is omitted.

Request body

json
{
  "body": "Synced to Linear ticket ENG-123",
  "authorName": "Automation",
  "authorEmail": "bot@acme.com"
}

Response

json
{ "comment": { … }, "thread": { … } }
DELETE /api/v1/threads/{id}

Permanently delete a thread, its comments, and any attached screenshot. Returns 204 No Content.

GET /api/v1/export

Export feedback as JSON or CSV. CSV returns one row per comment; threads without comments still appear as a single row.

Query parameters

  • format optional — json (default) or csv
  • website, status, from, to optional — Same filters as GET /threads

Example

bash
curl -s -H "Authorization: Bearer sk_…" \
  "https://suggestra.dev/api/v1/export?format=csv&from=2026-01-01" \
  -o suggestra-feedback.csv

Webhooks

Business plan accounts can register an HTTPS endpoint to receive real-time notifications when feedback changes. Configure your endpoint URL and subscribed events from Dashboard → Webhooks.

Setup

  • Enter your server URL (must accept POST with a JSON body).
  • Select which events to receive — you can enable any combination.
  • Suggestra generates a signing secret (whsec_…). Use it to verify the X-Suggestra-Signature header on every delivery.
  • Use Send test webhook in the dashboard to confirm your endpoint is reachable.

Delivery format

Each delivery is an HTTP POST with a JSON body and these headers:

Header Description
X-Suggestra-Event Event type (same as the event field in the body).
X-Suggestra-Delivery Unique delivery ID for deduplication.
X-Suggestra-Signature sha256=<hmac> — HMAC-SHA256 of the raw JSON body using your signing secret.
json
{
  "id": "evt_abc123…",
  "event": "thread.created",
  "createdAt": "2026-06-17T14:30:00+00:00",
  "data": {
    "thread": { "id": 42, "status": "open", "comments": [ … ], … }
  }
}

Return any 2xx status to acknowledge delivery. Non-success responses are logged but not retried automatically.

Event types

Event When it fires data payload
thread.created A visitor or reviewer pins new feedback on a website. thread — full thread object with nested comments.
comment.created A reply is added to an existing thread (embed, dashboard, or API). thread, comment
thread.status_changed Thread status is updated (open → resolved, etc.). thread, previousStatus
thread.deleted A thread is permanently deleted. thread — snapshot before deletion.
webhook.test Sent manually from the dashboard test button. message

Thread and comment objects match the shapes returned by the REST API (GET /api/v1/threads). See the endpoint reference above for field definitions.

Verifying signatures

Compute HMAC-SHA256 over the raw request body (before JSON parsing) and compare to the signature header:

php
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_SUGGESTRA_SIGNATURE'] ?? '';
$expected = 'sha256=' . hash_hmac('sha256', $payload, $signingSecret);

if (!hash_equals($expected, $signature)) {
    http_response_code(401);
    exit('Invalid signature');
}

Status transitions

Thread status can be open, resolved, reopened, or closed. Valid transitions:

From Allowed next statuses
open, reopened resolved, closed
resolved reopened, closed
closed reopened

Threads cannot transition back to open — use reopened instead. Invalid transitions return 400 Bad Request.

Errors

Errors use standard HTTP status codes with a JSON body when applicable:

Status Meaning
401 Unauthorized Missing or invalid API key.
403 Forbidden Valid key but account lacks Business API access.
404 Not Found Thread ID not found on your account.
400 Bad Request Invalid JSON, filter values, or status transition.
json
{
  "type": "https://tools.ietf.org/html/rfc2616#section-10",
  "title": "Bad Request",
  "status": 400,
  "detail": "Cannot change thread status from \"resolved\" to \"open\"."
}