Documentation
Integrating revws.
Drop-in reviews for SaaS products. Your users sign in to your product; revws federates that identity over a signed JWT and renders threaded reviews against the page they're on. You never store the data; you never write moderation UI.
Quickstart
Three things to copy
Every product gives you a public id (pub_…), a signing secret (sk_…), and a page id you choose per host page. Wire them in three steps.
- 1
Get your keys
Create a product in the dashboard. You get a public id (
pub_…) — safe to ship in client code — and a signing secret (sk_…) shown once. The secret stays on your server; rotate it any time. - 2
Mint a short-lived JWT per user
Sign an HS256 JWT with your signing secret in the same handler that renders the page. Your users, your identity — revws never owns accounts.
# Your backend mints a short-lived JWT per signed-in user. import jwt, time token = jwt.encode( { "iss": "pub_...", # your product id "sub": user.id, # YOUR user id "name": user.display_name, "exp": int(time.time()) + 600, }, PRODUCT_SIGNING_SECRET, # sk_... from the dashboard algorithm="HS256", ) - 3
Drop the tag
Add the script tag and a mount point. The widget mounts into
<div id="revws">, fetches the thread, and renders.<script src="https://cdn.revws.io/v1/widget.js" data-product="pub_..." data-page="<your page id>" data-user-token="<signed JWT>"></script> <div id="revws"></div>
Auth federation
One signed token per user
The widget needs a token that says "the user looking at this page is X". You mint it server-side and hand it to the widget. Every claim:
| Claim | Required | Description |
|---|---|---|
iss | yes | Your product's public id (pub_…) |
sub | yes | A stable id for the user in your system — never an email or PII |
name | optional | Display name shown next to the user's reviews |
exp | yes | Expiry — must be ≤ 15 minutes out |
The signing secret stays server-side, always. It never ships to a browser and never appears in the token — the widget only ever sees the signed JWT. Ten minutes is the sweet spot for token lifetime; the hard cap is fifteen. A bad secret or an over-15-minute exp is a 401 from every call.
REST API
Server-to-server
For SSR, native mobile clients, agents posting on a user's behalf, or any context where the widget isn't loaded. Base URL https://api.revws.io/v1. Auth is the same federated JWT the widget uses, passed as Authorization: Bearer <token>.
| Method | Path | Purpose |
|---|---|---|
| GET | /v1/threads | List threads for a (product, page). Usually one per page. |
| POST | /v1/reviews | Write a review. Body: { thread_id, parent_id?, body, rating? }. |
| POST | /v1/reactions | Toggle a reaction. Body: { review_id, emoji }. |
Ratings are a strict integer 1..5 on top-level reviews and forbidden on replies. Errors are { "detail": "<reason>" } with a 4xx status; a free workspace at its monthly cap gets 402 { "detail": { "code": "plan_limit_reached" } } until the next UTC month.
Widget embed
The embed contract
The widget reads its whole configuration from three data attributes on the script tag:
data-product- Your public id (
pub_…). data-page- Any string stable per host page. revws creates a thread per
(product, page_id). data-user-token- The signed JWT from your backend.
Default chrome is grayscale plus one accent — designed to disappear into your product. Theme it by overriding a single CSS variable, --revws-accent, on the mount point or any ancestor:
| Variable | Effect |
|---|---|
--revws-accent | Sets the widget's single accent color (stars, active controls, focus). |
The bundle is < 30 KB gzipped, reads from edge cache, and targets a < 150 ms p95 first paint.
Key rotation
Rotating the signing secret
Rotate from Settings → Danger zone → Rotate signing secret on the product page (workspace admins only), or via the REST call:
POST /api/admin/products/{pid}/secret/rotate
Authorization: Bearer <workspace admin token> Cutover is immediate — there is no dual-secret grace window. JWTs signed with the old secret stop verifying the moment you rotate. Update your server's REVWS_SIGNING_SECRET first, deploy, then rotate — or accept a brief window where signed-in users get a 401 until their page reloads with a fresh token. The new secret is shown once; every rotation and reveal writes an audit row.
Webhooks
Roadmap — not live yetEvent fanout
Nothing fires today. The contract below is a preview so you can plan for it; moderation runs in the dashboard for now.
When it ships: configure on your product's settings page. Every event — review.created, review.deleted, reaction.added, reaction.removed — is a signed POST to your endpoint, carrying an X-Revws-Signature: sha256=<hex> HMAC header (verify it before trusting the payload). The webhook secret is separate from the signing secret and rotates independently.
Ready to wire it up?
Private beta — keys granted within a day. No call, no demo gauntlet.