Developer docs
React SDK and Web Component
Render approved proof and ask customers for their story from inside your product. Both paths read the same signed manifest, set no cookies and call no third-party domains.
Packages
| Package | Use it for |
|---|---|
| @yousaid/react | ProofWall, ProofCarousel, ProofCard, ProofBadge, useProofRender, ProofPrompt, ProofPromptView, useProofPrompt and YouSaidProvider. Requires React 19 or later. |
| @yousaid/core | The framework-free client: fetchProofRender, the proof prompt client, resumable media uploads (uploadProofMedia, abortProofUpload) and the engagement beacon. |
| @yousaid/server | createYouSaidIdentityToken, which signs the end-user identity on your server. |
The packages are at version 0.1.0 and ship as TypeScript source.
Show proof with React
Create a placement in the workspace (or with the REST API), then pass its id. The component fetches the signed render manifest, shows only eligible items and shows any required incentive disclosure.
import { ProofWall } from "@yousaid/react";
export function Testimonials() {
return <ProofWall placementId="ppl_..." theme="auto" />;
}| Prop | Meaning |
|---|---|
| placementId | Required. The placement to render. |
| endpoint | API origin. Defaults to https://yousaid.dev. |
| manifest | A manifest you fetched on the server, for SSR or static pages. |
| fallback | A stored manifest used only when the live request fails, and only until its expiresAt. |
| theme | light, dark or auto. Overrides the placement's scheme. |
| preview | Disables analytics. Use it in staging and editors. |
| className | Class on the root element. |
| autoplayMs | ProofCarousel only. Default 7000. |
A signed manifest expires five minutes after it was generated. Do not cache it longer: that expiry is what keeps a withdrawn story from lingering in your pages.
Show proof with the Web Component
<script src="https://yousaid.dev/embed/yousaid-proof.js" defer></script>
<yousaid-proof placement="ppl_..." format="wall"></yousaid-proof>| Attribute | Meaning |
|---|---|
| placement | Required. A ppl_ id. |
| format | wall, carousel, card, video or badge. Overrides the placement format. |
| theme | light, dark or auto. |
| api | API origin. Defaults to the script's origin. |
| preview | No analytics. |
| eager | Load immediately instead of when scrolled into view. |
| timeout | Milliseconds before the signed fallback is used. Default 3000, clamped to 500 to 20000. |
| slot, audience (or segment), product, locale | Optional context for adaptive proof slots. Coarse and non-personal; the page path is sent only when one of these is set. |
| account | sha256 hex of your own account id, for CRM influence reporting. Never a raw id or email. |
Style it with the CSS custom properties --ys-accent, --ys-bg, --ys-fg, --ys-radius and --ys-font, and with ::part(root), ::part(card), ::part(quote), ::part(who), ::part(disclosure), ::part(cta) and ::part(badge). Custom CSS and markup inside the component are not accepted by design.
For strict-CSP or static sites, fetch the manifest on your server and either render it with the React components (manifest prop) or put it in a child script element:
<yousaid-proof placement="ppl_...">
<script type="application/json">{ "schema": "yousaid-proof-render-v1", ... }</script>
</yousaid-proof>Content Security Policy for an embedding site:
script-src https://yousaid.dev; connect-src https://yousaid.dev; img-src https://yousaid.dev; media-src https://yousaid.devAsk for a story in your product
The prompt opens the hosted collection flow for a published flow id (pfrm_). With a signed identity, YouSaid pre-fills who the customer is and applies request limits across your products and devices. The customer still chooses every field that is shown publicly.
import { YouSaidProvider, ProofPrompt } from "@yousaid/react";
// A function, not a string: every write consumes the token's nonce.
const getToken = () => fetch("/api/yousaid-token").then((response) => response.text());
<YouSaidProvider token={getToken}>
<ProofPrompt formId="pfrm_..." presentation="drawer" />
</YouSaidProvider>Presentations are inline, modal, drawer, popover and fullscreen. ProofPrompt defaults to drawer and its label defaults to "Share your story". useProofPrompt gives you the same state for a custom trigger.
Without React:
<script src="https://yousaid.dev/embed/proof-prompt.js" defer></script>
<yousaid-proof-prompt form="pfrm_..." presentation="inline" token-endpoint="/api/yousaid-token"></yousaid-proof-prompt>The element accepts form, presentation (default inline), origin, token-endpoint, label, frame-title and auto-open, or you can assign element.getIdentityToken. It dispatches yousaid:open, yousaid:step, yousaid:consent, yousaid:complete, yousaid:abandon and yousaid:close as CustomEvents.
Sign the end-user identity
Mint the token on your server with the product signing credential from your workspace. Never ship the secret to the browser.
import { createYouSaidIdentityToken } from "@yousaid/server";
const token = await createYouSaidIdentityToken(
{ sub: user.id, org_id: "org_...", product_id: "prod_...", email: user.email, name: user.name },
process.env.YOUSAID_SIGNING_SECRET,
{ keyId: process.env.YOUSAID_SIGNING_KEY_ID },
);- Always pass keyId. The token header must carry the key id of an active product credential; the helper's fallback value is not accepted by the server.
- Tokens are HS256 JWTs that last 600 seconds by default and at most 900.
- Each state-changing request consumes the token's nonce. Mint a fresh token per write; a reused one answers IDENTITY_REPLAYED.
- Requests from a browser are checked against the product's allowed origin.
- Rotating the signing credential keeps the previous key valid for an overlap window (24 hours by default, 0 to 168) so your servers can roll over.
Headless capture
If you build your own UI, start a session with POST /api/v1/proof/submissions/sessions and submit with POST /api/v1/proof/submissions. Both need a service API key with yousaid:proof:submissions:write. Submissions are always stored first, even when a plan allowance is used up. See the REST API.