Verify installations
The verify-script endpoint loads one of your pages and reports whether your workspace’s Grona snippet is on it. Call it from your backend before you set a campaign live, or in your own onboarding flow to tell a user their install is working. This page covers the request, what the check looks for, how Grona matches a URL to your website, and the cases where a working install reads absent.
Check a page
Section titled “Check a page”curl https://api.grona.ai/v1/websites/verify-script \ -H "Authorization: Bearer $GRONA_SECRET_KEY" \ -H "Content-Type: application/json" \ -d '{"url": "https://shop.example.com/"}'// verify.mjs (Node 18 or later)const res = await fetch('https://api.grona.ai/v1/websites/verify-script', { method: 'POST', headers: { Authorization: `Bearer ${process.env.GRONA_SECRET_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ url: 'https://shop.example.com/' }),});const { data } = await res.json();console.log(data.url, data.script.status, data.script.checkedAt);The call waits for the check to finish, then returns the page it checked and the result:
{ "data": { "url": "https://shop.example.com/", "script": { "status": "present", "checkedAt": "2026-10-20T09:28:43.162Z" } }, "meta": { "workspaceId": "65e8a1c47d2f9b03e6a15c42" }}url must be a full http or https page URL. Anything else returns 400 invalid_request.
What the check looks for
Section titled “What the check looks for”Grona loads the page in a headless browser and waits for it to finish loading. It then looks in the page’s <head>, and after that in its <body>, for a <script> element whose src ends with /workspaces/<your workspace id>/main.js. The workspace is the one your key belongs to, so a snippet from a different workspace reads absent.
The check covers only the page you send, not the rest of the site. It also does not check:
- that the anti-flicker script is present;
- that the snippet is placed first in
<head>, or that it keeps itsasyncattribute; - that the script loaded successfully or ran;
- that your consent setup allows it to run.
Statuses
Section titled “Statuses”The verify-script endpoint always returns a finished result:
script.status |
Meaning |
|---|---|
present |
The snippet was found on the page. |
absent |
The snippet was not found, or Grona could not load the page. |
A campaign also carries a script object, which reports the check of the campaign’s website. It can be in two more states, because that check runs in the background:
script.status |
Meaning | checkedAt |
|---|---|---|
checking |
A check is running. New campaigns start here. | null until a check has completed |
present |
The snippet was found. | When the check completed |
absent |
The snippet was not found, or Grona could not load the page. | When the check completed |
unknown |
No check is running and none has completed. New campaigns read checking, not unknown. |
null |
Before you set a campaign live, call verify-script for its page rather than relying on the campaign’s stored status.
Host matching
Section titled “Host matching”Grona works out which website a URL belongs to in one way, for both a campaign’s url and the url you send to verify-script. It reduces the URL to its host and ignores:
- the path;
- a trailing slash;
- a leading
www.; - letter case.
| URL | Website |
|---|---|
https://shop.example.com/ |
shop.example.com |
https://www.shop.example.com/dairy/whole-milk |
shop.example.com |
https://Shop.Example.com |
shop.example.com |
https://blog.example.com/ |
blog.example.com, a different website |
A campaign’s domain field holds the website’s origin, taken from its url, such as https://shop.example.com. Host matching only decides which website a URL belongs to. The page Grona loads and checks is always the exact URL you send, path included, so check a page the campaign will run on.
Why a working install can read absent
Section titled “Why a working install can read absent”The check reads the page the way a first-time visitor’s browser would, so some setups hide the snippet from it even though visitors get Grona.
- A suffix on the script URL. Optimisation and caching plugins often add a version or cache-busting suffix, such as
main.js?ver=6.4. Thesrcthen no longer ends withmain.js. Exclude the Grona snippet from the plugin’s script handling. - A consent tool holds the script back. Some consent tools rewrite scripts as
type="text/plain"with adata-srcattribute until the visitor accepts. The check never accepts, so it sees no Grona script. See Consent and storage. - The script is added late. A tag manager or a delayed-loading plugin can add the snippet after the page has loaded, too late for the check. Install the snippet directly in
<head>where you can. See Google Tag Manager. - Grona cannot load the page. Pages behind a login, a password, a region block or bot protection read
absent. - The wrong page. The check covers one URL. A snippet installed only on some templates reads
absenton the others.
Next steps
Section titled “Next steps”- Install the Grona script: the snippet and where it goes.
- Create campaigns from your platform: the end-to-end flow that starts with this check.