Widgets
Drop-in feedback, waitlist, changelog, inbox and chat widgets — a script tag on your page, endpoints already behind it.
Five small features that every app wants and nobody wants to build: a feedback box, a waitlist, a changelog, a per-user notification inbox, and a chat box. Each is a hosted script plus a public endpoint on the platform — the console's Widgets page hands you a ready-made snippet per widget.
Adding one
<script src="https://camplax.dev/cdn/feedback.js"
data-project="<slug>" data-api="<api origin>"
data-key="wx_…" data-turnstile-sitekey="…" data-screenshot></script>
Paste the snippet where the widget should live. Some widgets also need a mount point — <div id="camplax-waitlist">, <div id="camplax-changelog">, <div id="camplax-inbox"> — and the snippet says so. The data-key is your project's publication key: deliberately public, it only binds the embed to your project. Anonymous writes are protected the real way — the Origin must be a hostname that is live for your project, every action is rate-limited per IP, and each mutation needs an invisible Turnstile token the script obtains itself.
Server-side calls skip the publication key entirely and use a project API key with widgets:read or widgets:write scope.
The five
Feedback. A bug / idea / praise box with an optional 1–5 rating, email and screenshot. Entries land on the Widgets page and emit a $feedback event into analytics.
Waitlist. Email capture with a queue position. Optional double opt-in (the widget mails a confirm link that lands on a hosted confirm page), disposable-domain blocking, referral ids, and a showPosition switch. A confirmed join emits $waitlist_join.
Changelog. You write entries in Markdown on the Widgets page — publish and unpublish per entry — and the widget renders sanitized HTML from GET /v1/w/<slug>/changelog. Fifty entries per fetch.
Inbox. Per-user notifications. The sensitive part is done properly: your server mints a 15-minute per-user token with POST /v1/w/<slug>/inbox/access-token (a widgets:write key — which never reaches the browser), and the script reads only that user's items with GET /v1/w/<slug>/inbox. Create notifications server-side with POST /v1/w/<slug>/inbox (user, title, optional body and href).
Chat. A visitor message box that stores threads and replies politely — today the reply is a canned acknowledgment (escalation wording is a setting). Honest scope: it records the conversation; it is not yet a support agent.
There is also a dropzone script — file upload that uses the same signed upload-URL machinery as storage, bound to the size and type you allow, with active formats (SVG, HTML, JS) refused.
Settings and limits
Per project, in the console: chat options (collect email, escalate wording, signed-in-only), waitlist options (double opt-in, show position, referrals, disposable block) and dropzone limits (max bytes up to 100 MB, max files, accept list).
Public endpoints are rate-limited per action — feedback and waitlist joins at 5/hour per IP, chat at 8/minute, inbox reads at 60/minute. The full surface:
| Method | Path | Caller |
|---|---|---|
POST | /v1/w/<slug>/feedback | Publication key or widgets:write |
POST | /v1/w/<slug>/waitlist | Same — plus the hosted /waitlist/confirm link |
GET | /v1/w/<slug>/changelog | Publication key or widgets:read |
POST | /v1/w/<slug>/chat | Publication key or widgets:write |
GET/POST | /v1/w/<slug>/inbox | Per-user token, or widgets:read/widgets:write |
POST | /v1/w/<slug>/inbox/access-token | widgets:write only |
POST | /v1/w/<slug>/dropzone/upload-url | Publication key or widgets:write |
Under the hood: each script is a single-file bundle served from camplax.dev/cdn, and every write lands in platform tables owned by your project.