SitepingConfig is a discriminated union over the two transport modes — endpointorstore, never both, never neither. TypeScript rejects the invalid combinations at compile time instead of leaving you to discover them at runtime:
initSiteping({ endpoint: "/api/siteping", store, projectName: "x" }); // ✗ both modesinitSiteping({ store, apiKey: "sk-…", projectName: "x" }); // ✗ HTTP-only option in store modeinitSiteping({ projectName: "x" }); // ✗ no transport
Option
Type
Default
What it does
endpoint
string
—
HTTP mode. URL of your SitePing API (e.g. /api/siteping)
store
SitepingStore
—
Store mode. Write to a store in the browser instead of HTTP — excludes endpoint, apiKey and headers
projectName
string
—
Required in both modes. Scopes everything the widget reads and writes
apiKey
string
—
HTTP mode only. Sent as Authorization: Bearer <key> on every request
headers
object or function
—
HTTP mode only. Extra headers, static or computed per request (sync or async). Merged last, so an explicit Authorization here beats apiKey
The two halves are exported as SitepingHttpConfig and SitepingStoreConfig, which is what you want when a wrapper component takes the config as a prop and only supports one mode:
import type { SitepingHttpConfig } from "@siteping/widget";function mountFeedback(config: SitepingHttpConfig) { /* endpoint guaranteed */ }
apiKey ships in your client bundle — every visitor can read it. Use it for internal tools already behind a login; on public sites prefer headers with a function that returns a short-lived token.
Bypasses the production and viewport guards (never the SSR guard)
minViewportWidth
number
0
Below this width the widget skips with onSkip("mobile"). The default renders at every width — phones get the compact layout. Before 0.11 the default was 768: set it to keep the widget off phones
position
"bottom-right" | "bottom-left"
"bottom-right"
Corner for the floating button
showAnnotationsToggle
boolean
true
Set false to remove the marker-visibility toggle from the radial menu
Attach recent console entries (max 50) and failed network requests (max 20, URL recorded without credentials, query string or hash) to each feedback. true enables both; an object picks channels and lower limits (maxConsoleEntries / maxNetworkEntries — larger values are clamped to 50 / 20, the server's caps)
deepLink
boolean or { param?: string }
false
On first load, focus the feedback referenced by ?siteping=<id> (or your custom param). Initial load only — for route changes, call focusFeedback()
identity
{ name, email }
—
Pre-fill author fields from the host app's session (e.g. SSO, or Better Auth): skips the identity modal, never persisted to localStorage. It does not authenticate API requests — a server access policy does, from your session cookie on the same origin or a bearer token sent with headers. Read at init: remount the widget after an account change
panelActions
array
—
Your own buttons and links in the feedback detail view — create a ticket, open it in your tracker. See Panel actions
Hide what triages feedback — resolve, reopen, delete, the bulk actions, "Delete all". Creating, browsing and replying stay
For a client who reviews while your team triages. On top of it, the widget hides what the server says this visitor may not do — its permissions — so a public widget in front of an apiKey offers no button that would answer 401, even without readOnly. Either way it only hides: the server decides what it accepts.
Only show markers created on the current page. Filtered both server-side and client-side, so annotations can't leak across pages
getPageScope
() => { url, urlPattern }
pathname
Customize what "the current page" means — return a stable url (and optionally a template like /products/:id as urlPattern) so dynamic routes share their feedback
watchNavigation
boolean
true
Re-fetch feedbacks on SPA navigation (History API patch + popstate/hashchange). Data only — it never scrolls or refocuses. Set false and call refresh() yourself to opt out
The default page scope is window.location.pathname — by construction it contains no query string, so tokens or search params in the URL never reach the server. If you provide getPageScope, keep that property: return a path or a template, not location.href.