Headless storefronts
Evident does not require a BigCommerce or Shopify theme. If your storefront is React, Next.js, Vue, Astro, or anything else that renders HTML, you can run galleries, FAQs and reviews on it with a script tag and an allow-listed origin.
Get your Store Environment ID
In Evident, go to Settings → Store. Copy the Store Environment ID. This is the only credential the storefront widgets need — there is no separate storefront token to request, and nothing secret to keep on a server. It identifies which store a request belongs to; what controls who may call the API is the origin allow-list in the next step.
Allow-list the origins you render from
Still under Settings → Store, add every origin your storefront runs on to Allowed Storefront Origins — production, staging, and http://localhost:3000 for local development. Changes take effect immediately; no redeploy on our side.
https://yourstore.com
https://staging.yourstore.com
http://localhost:3000 An origin is scheme + host + port only — no paths, no trailing slash. A bare domain is treated as https. Preview deployments are a common trip-up: every Vercel or Netlify preview URL is a different origin and will be blocked until you list it.
Load the SDK
Add the script once, anywhere in your app shell or root layout. It reads your Store Environment ID from the data attribute, pulls in its own stylesheet, and mounts every widget container it finds — including ones your framework renders later.
<script
src="https://app.evidentugc.com/widgets/evident-sdk.min.js"
data-store-env-id="YOUR_STORE_ENV_ID"
></script> Place a widget container
Widgets mount into any element carrying a data-evident-widget attribute. A gallery needs only its slug, which you will find in Evident under Galleries:
<div
data-evident-widget="gallery"
data-gallery-slug="customer-photos"
data-columns="3"
></div> Galleries, FAQs and store-wide review widgets work immediately. Product-scoped widgets — star badges, per-product review lists, review forms — need your catalog in Evident first. See step 06.
React, Next.js, Vue and other SPAs
The SDK watches the DOM, so a container your framework renders after page load is mounted automatically — and if a re-render wipes it, it is re-mounted. If you would rather mount explicitly than rely on that, the SDK exposes an API. window.Evident may not exist yet on first paint, which is why the optional chaining below matters — the container still gets picked up when the script finishes loading.
'use client';
import { useEffect, useRef } from 'react';
export function EvidentGallery({ slug }) {
const ref = useRef(null);
useEffect(() => {
const el = ref.current;
window.Evident?.mount(el);
return () => window.Evident?.unmount(el);
}, [slug]);
return (
<div
ref={ref}
data-evident-widget="gallery"
data-gallery-slug={slug}
/>
);
} Full API: refresh() re-scans and mounts anything blank, mount(el) mounts one container, unmount(el) tears one down, and init() is safe to call more than once — React StrictMode double-invokes effects in development.
Push your catalog (for product-scoped widgets)
Platform stores sync their catalog automatically. A headless store pushes it instead. Create an API key under Settings → API Keys, then upsert products — up to 250 per call, keyed on your own product ID, so re-sending the same payload is safe.
curl -X PUT https://api.evidentugc.com/api/v1/products \
-H "Authorization: Bearer evnt_YOUR_API_KEY" \
-H "x-store-env-id: YOUR_STORE_ENV_ID" \
-H "Content-Type: application/json" \
-d '{
"products": [
{
"platformProductId": "sku-1024",
"name": "Merino Wool Runner",
"imageUrl": "https://cdn.example.com/mwr.jpg",
"productUrl": "https://example.com/p/merino-wool-runner",
"price": 98.00
}
]
}' This is a replace, not a merge: a field you omit is cleared, so send the whole product each time. Items are independent — one rejected row does not fail the batch, so check the failed count in the response rather than the status code alone. The platformProductId you send here is the value your widgets pass as data-product-id.
Push your orders (for review requests and verified purchase)
Orders are what connect a reviewer to a purchase. Push them and you get the verified-purchase badge plus automated review request emails. Push your catalog first — line items link to products by platformProductId, and verified-purchase matches on that link, so an order whose products are missing is stored but inert. The response tells you how many line items are still unlinked.
curl -X PUT https://api.evidentugc.com/api/v1/orders \
-H "Authorization: Bearer evnt_YOUR_API_KEY" \
-H "x-store-env-id: YOUR_STORE_ENV_ID" \
-H "Content-Type: application/json" \
-d '{
"scheduleReviewRequests": false,
"orders": [
{
"platformOrderId": "order-5581",
"customerEmail": "[email protected]",
"customerFirstName": "Dana",
"status": "Shipped",
"orderDate": "2026-08-01T14:22:00.000Z",
"lineItems": [
{ "platformProductId": "sku-1024", "quantity": 2, "price": 98.00 }
]
}
]
}' scheduleReviewRequests defaults to false, which makes backfilling your order history safe — nothing is emailed. Turn it on only once you are pushing orders as they happen. Orders older than 30 days are never scheduled even with it on. Review requests also trigger on the statuses configured under Settings ("Shipped" by default), so the status you send decides eligibility.
What to know before you build
Headless support is not yet at parity with the platform integrations. These are the gaps worth knowing about up front rather than discovering mid-build.
Review requests are opt-in, and only for recent orders
Pushing an order never emails anyone unless you set scheduleReviewRequests. Even then, orders older than 30 days are skipped: the send delay is counted from when you push, not from the order date, so a backfilled order would otherwise be treated as if it shipped today.
Content Security Policy
If you enforce a CSP, allow app.evidentugc.com for script-src and style-src, and cdn.evidentugc.com for img-src. A report-only policy will log violations without blocking — the widget will stop rendering the moment you enforce it.
Script tag, not a package
The SDK is distributed as a script tag rather than an npm package today. It is framework-agnostic and needs no build step, but there is no typed React component to import — wrap it yourself as shown in step 05.
Troubleshooting
The widget renders nothing and the console shows a CORS error
The origin you are rendering from is not on the allow-list. Copy it exactly as the browser reports it — including the port — into Settings → Store → Allowed Storefront Origins. Note that curl cannot reproduce this: it sends no Origin header, so the request is never checked and always appears to succeed. Reproduce it with -H "Origin: https://yourstore.com".
A product widget returns 404
The product does not exist in Evident yet. Product-scoped widgets resolve through the platformProductId you push in step 06, and 404 until that product has been upserted. Galleries and FAQs do not depend on your catalog.
The widget disappears after navigating between pages
Make sure you are on the current SDK build — the script is served with must-revalidate, so a hard reload is enough to pick it up. If you mount explicitly, confirm your cleanup calls unmount(el) with the same element you passed to mount(el).
Building something custom?
Tell us what your storefront needs and we'll tell you honestly whether Evident covers it today.