Environments & safe testing
There is one environment. How to build against it without emailing a real customer, and what you can and cannot clean up afterwards.
7 min read Last updated
On this page
Evident has one environment. There is no sandbox, no test mode and no separate set of test credentials. An organization has one storefront, and that storefront has one environment — the API you build against is the API your live widgets use, and the data you create while building is real data in your real account.
That is worth knowing before you start rather than after, so this page says it first. The rest of it is how to build safely anyway, which is entirely possible: the controls you need already exist. You just have to know which ones they are.
What can reach a customer
Very little, but not nothing. A test product shows on your storefront only where a widget asks for its ID, so pushing TEST- products is invisible to shoppers. Galleries, FAQs and widget styling are different: if your live site already embeds a gallery or store-wide FAQs, a change to them is live the moment you save it.
The exception is review request emails. Orders trigger them, and a pushed order carries a real email address. Three separate guards stand in the way, and all three are on by default:
- Review request emails are off for a new store. Nothing is sent until someone ticks Enable review request emails under Settings → Store → Review Settings → Configure.
- Pushing an order schedules nothing unless you ask.
PUT /api/v1/ordersonly schedules a request when the body setsscheduleReviewRequests: true. - Old orders are skipped. Even with the flag set, an order dated more than 30 days ago is never scheduled — the send delay counts from when you push, not from the order date, so a backfill cannot mail six months of customers.
So the rule is simple: leave scheduleReviewRequests off until you are pushing live orders as they happen, and put addresses you own on test orders.
Two other things follow from a pushed order, and they are worth knowing about even though neither reaches a shopper:
- Outbound webhooks. The same flag decides whether a pushed order fires
order.placedandorder.completed. With it off, your webhook subscribers hear nothing about a backfill. - Loyalty points. If loyalty is running, qualifying orders earn points on a sweep every ten minutes whatever the flag says — see Earning rules. A test order dated after your orders start date earns points for its test address, and that member shows up in your loyalty lists.
Staging and local development
You do not need a second environment to run a staging site or a local build against Evident. You list them on the one you have.
Under Settings → Store → Allowed Storefront Origins, add every origin you render from:
https://yourstore.com
https://staging.yourstore.com
http://localhost:3000
Changes take effect immediately. Every origin on the list renders the same store’s data, so staging shows your real reviews — which is usually what you want from a staging site.
Two things that will cost you an hour if nobody tells you:
- Preview deployments are separate origins. Every Vercel or Netlify preview URL is its own origin and is blocked until listed. If your team ships a preview per pull request, decide early whether to give previews a stable alias rather than chasing generated URLs.
curlcannot reproduce a CORS failure. It sends noOriginheader, so the request succeeds and tells you nothing. Reproduce in a browser.
See Headless storefronts for the full rules on what counts as an origin.
Give development its own API key
One key per integration is good practice anyway — API keys & scopes makes the case — and it matters more while you are building, because development keys end up in places production keys do not: laptops, CI logs, screenshots in a ticket.
When you create it under Settings → API Keys:
- Pick the lowest role that works. A
VIEWERkey is refused on every write, so it is the right choice for anything that only reads. Pushing products and orders needsADMIN—MEMBERdoes not include writing the catalogue or orders. - Then narrow it with Access. Because an ingest key has to be
ADMIN, this is what actually limits it: Only the resources I choose, with read & write on products and orders and nothing else. - Set an expiry. A development key that stops working in 30 days is one you cannot forget about. Expiry is a hard stop — requests get a
401with no warning beforehand — so put the date in your calendar.
If a development key leaks, revoke it. Revocation takes effect on the next request, and nothing else using a different key notices.
Never put an evnt_ key in browser code. The widget SDK deliberately does not use one — it identifies itself with the store environment ID and is authorised by the origin allow-list. If you find yourself wanting a key in front-end code, what you want is the SDK.
Push test data that looks like test data
Products are safe to push as often as you like. PUT /api/v1/products is an upsert keyed on your own platformProductId, so re-sending the same payload changes nothing. It replaces rather than merges — a field you omit is cleared — so send the whole product every time.
Use identifiers that are obviously not real, so anything that leaks into a screenshot or a support ticket is self-evidently test data:
{
"products": [
{
"platformProductId": "TEST-sku-0001",
"name": "TEST — Merino Wool Runner",
"imageUrl": "https://cdn.example.com/test-mwr.jpg",
"productUrl": "https://example.com/p/test-merino-wool-runner",
"price": 98.00
}
]
}
Then orders, with the flag set explicitly rather than left to the default:
curl -X PUT https://api.evidentugc.com/api/v1/orders \
-H "Authorization: Bearer evnt_YOUR_DEV_API_KEY" \
-H "x-store-env-id: YOUR_STORE_ENV_ID" \
-H "Content-Type: application/json" \
-d '{
"scheduleReviewRequests": false,
"orders": [
{
"platformOrderId": "TEST-order-0001",
"customerEmail": "[email protected]",
"customerFirstName": "Test",
"status": "Shipped",
"orderDate": "2026-10-01T14:22:00.000Z",
"lineItems": [
{ "platformProductId": "TEST-sku-0001", "quantity": 1, "price": 98.00 }
]
}
]
}'
Three things about that request:
scheduleReviewRequests: false, written out. It costs one line, and it is the line that stops mail going out if the store’s review request emails are already switched on.- Plus-addressing on a domain you control.
[email protected]reaches your own inbox, so when you do switch emails on you read exactly what a customer would receive. Never use a customer’s real address. - Products before orders. Line items link to products by
platformProductId, and verified-purchase matching depends on that link. WatchunlinkedLineItemsin the response fall to zero.
When you are ready to send real emails
In this order:
- Authenticate your sending domain — Sending domain & deliverability. Do it before the first real send, not after; reputation damage from an unauthenticated domain is the hardest kind to undo.
- Switch review request emails on under Settings → Store → Review Settings → Configure.
- Send one to yourself. Push a single, recent order with your own address, status
ShippedandscheduleReviewRequests: true. It goes out after the delay set in the same dialog — 14 days unless you change it, and one day at the shortest — so lower it while you test, then put it back. If you connected Klaviyo or Omnisend, Evident sends that platform an event instead, and the email is whatever its flow sends. - Then turn the flag on for live orders, once you are pushing them as they happen rather than in backfills.
Review requests trigger on the order status — Shipped by default — so an integration that sends Processing will look broken when it is merely ineligible. Headless storefronts covers why a headless store stays on that default.
Testing the widgets and webhooks
The widgets need no key and no test mode, only your development origins on the allow-list above. If one loads and shows nothing, Troubleshooting the widgets walks through the usual causes in the order worth checking them.
Webhooks deliver to any URL you register, so a tunnel to your own machine works for development. Delivery is fire-and-forget with a 15-second timeout and no retry, which means a tunnel that drops while an event fires loses that event. Outbound webhooks covers delivery and how to build a receiver that tolerates it.
Cleaning up
What you can remove, and what you cannot:
| Test data | How to remove it |
|---|---|
| Reviews | Delete them one at a time from Reviews |
| Products | There is no delete. Re-push them with "isActive": false and their widgets stop showing |
| Orders | There is no way to delete an individual order |
| Gallery items, FAQs | Remove them from their own screens in the admin app |
There is one reset, and it is total. Settings → Integrations → Manage → Delete Store & All Data permanently deletes the store and everything in it — products, orders, reviews, loyalty members, galleries — after you type DELETE to confirm. Reconnecting creates a new store with a new store environment ID, so every widget embedded with the old one stops working until you update the script tag. It is not a way to tidy up a live store.
That is why the identifiers above start with TEST-: on a store you intend to keep, test data you cannot delete should at least be impossible to mistake for the real thing.
The short version
| While building | Once live | |
|---|---|---|
| Environment | The one you have | The same one |
| Origins | localhost, staging and preview URLs added | Remove the ones you no longer use |
| API key | Its own, lowest workable role, scoped, with an expiry | Its own, one per integration |
scheduleReviewRequests | false, written out | true, once pushing live orders |
| Review request emails | Off | On, after the sending domain is authenticated |
| Customer emails on orders | Plus-addressed, on a domain you own | Real |
| Identifiers | TEST- prefixed | Real |
Related
- Headless storefronts — the full integration path
- API keys & scopes — roles, scopes, expiry and revocation
- Troubleshooting the widgets — when a widget renders nothing
- Sending domain & deliverability — before your first real send
Something missing or out of date? Email [email protected] — docs corrections go straight to the team that builds the feature.