Register your origin, set your CSP, know what is sent
Register your site’s origin or every request fails
Section titled “Register your site’s origin or every request fails”There is no secret in the browser; the publishable key is an address, not a credential. Anything shipped in a bundle is public, so access is bounded by origin instead: your site’s origin has to be registered against your organization.
The dashboard (Settings → Origins) is the usual place to do it. The API works too, authenticated as your organization. The endpoint refuses any session that does not belong to the org, so nobody can register an origin against your organization id from the outside:
curl -X POST "https://<your-api>/api/organizations/<org-id>/embed-origins" \ -H "Content-Type: application/json" \ -H "Cookie: <your dashboard session>" \ -d '{"origin":"https://yoursite.com"}'Until it is, every request fails CORS and your visitors read your fallback copy.
Register each origin you actually serve from, including staging and your local
dev server. Wildcards are rejected: a browser never sends one, so such an entry
would look like coverage while covering nothing. Plaintext http is accepted
for localhost only.
Add three CSP directives
Section titled “Add three CSP directives”If you run a Content Security Policy:
connect-src https://<your api host> # content and the copy streamimg-src https://<your CDN host> # campaign artwork, if anymedia-src https://<your CDN host> # video creativesThe SDK loads no script from anywhere. The one exception is audio playback: if
you call mountAudio with a Spotify track, Spotify’s own iframe API script is
injected into your page, which needs:
script-src https://open.spotify.comframe-src https://open.spotify.comTreat that call as an explicit decision to bring in a third party.
Exactly what leaves the browser
Section titled “Exactly what leaves the browser”All of this goes to your SparkleTree API and nowhere else. Disclosing it to your visitors is your obligation, so here it is plainly:
| Sent | Where it comes from |
|---|---|
| Time of day, day of week, timezone | The device clock. |
| Language, coarse mobile/desktop flag | navigator and a media query. |
| Referring origin | document.referrer, reduced to its origin. |
| A per-tab session id | Random, kept in sessionStorage. |
| Impressions and clicks | With the campaign, the version served, where the copy came from, and whether adapted copy was on screen. |
Anything you pass as context | Yours. |
Only the origin of the referrer is sent, never the full URL. Referrer paths and query strings routinely carry session tokens and personal data, and that value would end up in server and CDN logs.
Your page content is not in that list and the API takes no parameter for it: nothing is read out of your DOM, so the copy on your page stays yours alone. There is also no geolocation prompt, no fingerprinting, no third-party analytics, and no cross-site identifier.
Drop a signal you do not want to send
Section titled “Drop a signal you do not want to send”Your context is merged over the collected signals and wins, so overriding one
with undefined removes it:
<SparkletreeProvider publishableKey={KEY} context={{ referrer: undefined }}>What the storage prop gates
Section titled “What the storage prop gates”storage controls where the visitor’s “show me the standard version” preference
is kept. Pass your own store if a consent manager gates localStorage, or if
your test environment does not provide one.
That preference is its whole scope. The per-tab session id and the impression and click events are sent regardless; to stop those, do not render the components.
Fragment reads are public and cacheable
Section titled “Fragment reads are public and cacheable”The fragment read request carries no credentials and no prose from your page, only three identifiers: your organization id, the context hash, and the schema hash. That is why it can be served from a shared cache. Your fallback strings never appear in a URL. The write request, which spends credits, is the one that takes a signed token.