SDK Installation Verification FAQ
After installing the Break Ground SDK on your website, the dashboard verifies the installation in three steps:
- Script Loaded — any event received (proves the API key works and the script loaded)
- SDK Initialized — an
SDK_INITevent received (provesDAP('init', ...)was called) - Events Flowing — behavioral events received (proves data capture is working)
If verification is stuck on one of these steps, find your symptom below.
"Script not loaded" — Step 1 fails
No events are reaching the API at all.
Wrong or missing API key
The tenantKey in your snippet must exactly match an active API key from Settings > API Keys. A typo, an empty string, or a revoked key will silently fail.
How to fix:
- Go to your Break Ground dashboard → Settings > API Keys
- Copy the full API key (it starts with
wfx_) - Replace the
tenantKeyvalue in your snippet:DAP("init", {tenantKey: "wfx_abc123...",baseUrl: "https://api.yourapp.com",}); - Redeploy your site and click Check Again in the verification panel
Missing or incorrect baseUrl
If you omit baseUrl, the SDK defaults to a placeholder URL (https://api.dap.example.com) that does not exist.
How to fix:
-
Go to your Break Ground dashboard → Settings > Installation
-
Copy the full snippet — it includes the correct
baseUrlfor your environment -
Replace your existing snippet with the copied one
-
Verify the
baseUrlstarts withhttps://and has no trailing slash:// CorrectDAP("init", { tenantKey: "wfx_...", baseUrl: "https://api.yourapp.com" });// Wrong — trailing slashDAP("init", { tenantKey: "wfx_...", baseUrl: "https://api.yourapp.com/" });// Wrong — missing baseUrl entirely (defaults to placeholder)DAP("init", { tenantKey: "wfx_..." });
Content Security Policy (CSP) blocking the script
How to diagnose:
- Open your website in Chrome/Edge → press
F12→ go to the Console tab - Look for an error like:
Refused to load the script 'https://...' because it violates the followingContent Security Policy directive: "script-src 'self'"
- If you see this error, your CSP is blocking the SDK
How to fix:
- Find your CSP header — it's typically set in one of these places:
- A
<meta http-equiv="Content-Security-Policy">tag in your HTML<head> - An HTTP response header (
Content-Security-Policy) set by your web server or CDN (e.g., nginx config, Cloudflare headers, Vercelvercel.json)
- A
- Add the SDK script domain to the
script-srcdirective. For example, if your SDK is hosted athttps://cdn.breakground.io:script-src 'self' https://cdn.breakground.io; - Also add the API domain to
connect-srcso the SDK can make API calls:connect-src 'self' https://api.yourapp.com; - If you self-host the SDK (bundled with your app), you only need the
connect-srcchange
Ad-blockers or privacy extensions
Browser extensions like uBlock Origin, Privacy Badger, or Brave Shields can block third-party scripts and network requests.
How to diagnose:
- Open your website in a private/incognito window with all extensions disabled
- Check if the SDK verification passes now
- If it passes in incognito but not in a normal window, an extension is blocking it
How to fix:
The best solution is to self-host the SDK so it's served from your own domain:
- Download the SDK bundle from npm:
npm install @breakground/runtime-sdk - Include the SDK file from your own static assets instead of a CDN
- This makes the SDK indistinguishable from your own code to ad-blockers
If self-hosting isn't an option, document for your end users that they should allowlist your site in their ad-blocker.
Script tag placement
How to diagnose:
- Open your website → press
F12→ go to the Console tab - Type
typeof DAPand press Enter - If it returns
"undefined", the SDK script hasn't loaded yet
How to fix:
Use the exact snippet from Settings > Installation — it includes a command queue that handles load ordering automatically. If you're placing the script manually, ensure this order:
<!-- 1. Load the SDK script FIRST (no async/defer) -->
<script src="https://cdn.breakground.io/sdk.js"></script>
<!-- 2. Initialize AFTER the script tag -->
<script>
DAP("init", { tenantKey: "wfx_...", baseUrl: "https://api.yourapp.com" });
</script>
If you must use async, use the command queue pattern:
<script>
// Command queue — buffers calls until the SDK loads
window.DAP =
window.DAP ||
function () {
(window.DAP.q = window.DAP.q || []).push(arguments);
};
DAP("init", { tenantKey: "wfx_...", baseUrl: "https://api.yourapp.com" });
</script>
<script async src="https://cdn.breakground.io/sdk.js"></script>
"SDK not initialized" — Step 2 fails
The script loaded, but the SDK_INIT event was never received.
DAP('init', ...) never called
How to diagnose:
- Open your website → press
F12→ go to the Console tab - Type
typeof DAP— if it returns"function", the script loaded - Search your page source (
Ctrl+U) forDAP('init'— if it's missing, the init call was never added
How to fix:
Add the init call after the SDK script tag. The snippet from Settings > Installation includes both the script tag and the init call — copy the full snippet, not just the <script src> tag.
baseUrl pointing to the wrong environment
How to diagnose:
- Open your website → press
F12→ go to the Network tab - Filter by
/api/sdk/config - Check the request URL — does the domain match your Break Ground dashboard URL?
- Dashboard at
https://app.breakground.io→ API should behttps://api.breakground.io - Dashboard at
https://app.staging.breakground.io→ API should behttps://api.staging.breakground.io
- Dashboard at
How to fix:
- Go to the correct dashboard environment → Settings > Installation
- Copy the snippet (it has the matching
baseUrl) - Replace the snippet on your website
API endpoint unreachable
How to diagnose:
- Open your website → press
F12→ go to the Network tab - Filter by
/api/sdk/config - Look at the request status:
- No request at all → the init call isn't running (see sections above)
- Status
(failed)orERR_NAME_NOT_RESOLVED→ DNS can't resolve the API domain - Status
(failed)withERR_CONNECTION_REFUSED→ API server is down or port is blocked CORS errorin console → see "CSP blocking" section above
How to fix:
- Test from your terminal to rule out browser issues:
curl -v https://your-api-domain/health
- If curl also fails:
- Verify the domain is correct (no typos)
- Check if your network/firewall allows outbound HTTPS to the API domain
- Check the Break Ground status page for outages
- If curl succeeds but the browser fails, it's a CSP or CORS issue — see the CSP section above
JavaScript errors before init()
How to diagnose:
- Open your website → press
F12→ go to the Console tab - Look for red error messages that appear before any
[DAP]log entries - An uncaught error in a script that runs before the SDK init call will halt JavaScript execution
How to fix:
- Fix the unrelated JavaScript error first — the error message and stack trace in the console will point to the source
- Alternatively, move the SDK snippet to a separate
<script>tag so other script errors don't block it:<!-- Isolated — errors in other scripts won't affect this --><script src="https://cdn.breakground.io/sdk.js"></script><script>DAP("init", { tenantKey: "wfx_...", baseUrl: "..." });</script>
SDK loaded asynchronously with early init call
How to diagnose:
- Check your page source — if the SDK
<script>tag hasasyncordefer, and theDAP('init')call is in a separate inline<script>above or alongside it, the init call may run before the SDK loads - In the Console, look for
DAP is not definedorDAP is not a function
How to fix:
Use the command queue pattern (see the "Script tag placement" fix above) — this ensures init calls are buffered and replayed after the SDK loads.
Init timeout too low
How to diagnose:
- Enable debug mode by adding
debug: trueto your init config - Look in the Console for a message like:
SDK init timed out - Check the Network tab — is the
/api/sdk/configrequest taking longer than yourinitTimeoutMsvalue?
How to fix:
Either remove the custom initTimeoutMs (the default is 10 seconds, which is sufficient for most networks) or increase it:
DAP("init", {
tenantKey: "wfx_...",
baseUrl: "...",
initTimeoutMs: 15000, // 15 seconds
});
If the config request consistently takes more than 10 seconds, the issue is likely network latency to the API — consider whether a CDN or regional endpoint is available.
"Events not flowing" — Step 3 fails
The SDK initialized successfully, but no behavioral events (page views, clicks) are being received.
Domain mismatch (most common)
If your API key is bound to a specific site, the SDK must be loaded from a matching domain.
How to diagnose:
- Open your website → press
F12→ go to the Network tab - Filter by
/api/sdk/events - If you see
401responses, click the request → go to the Response tab - Look for the error code
AUTH_ORIGIN_MISMATCH
How to fix:
- Go to your Break Ground dashboard → Settings > Sites
- Find the site linked to your API key
- Check the Domain field — it must match your website's domain exactly:
- Your site is
www.example.com→ domain must bewww.example.com, notexample.com - Your site is
app.example.com→ domain must beapp.example.com
- Your site is
- If you need both
example.comandwww.example.comto work, enable the Allow Subdomains toggle for that site - Alternatively, go to Settings > API Keys and use a tenant-wide API key (not bound to any specific site)
API key revoked or expired
How to diagnose:
- Open your website → press
F12→ go to the Network tab - Filter by
/api/sdk/— if all requests return401, the API key is invalid
How to fix:
- Go to your Break Ground dashboard → Settings > API Keys
- Check if the key listed in your snippet is still in the table and shows Active status
- If the key is missing or revoked, create a new API key:
- Click Create API Key
- Optionally bind it to a site
- Copy the new key (you won't be able to see it again)
- Update your snippet's
tenantKeywith the new key
- Redeploy your site
Tenant account inactive
How to diagnose:
- Check the Network tab for
401responses with error codeAUTH_TENANT_INACTIVE - Try logging into the Break Ground dashboard — you may see a suspension notice
How to fix:
Contact Break Ground support or check your billing status. Account suspension is typically caused by an expired subscription or a billing issue. Once the account is reactivated, the SDK will resume sending events automatically — no code changes needed.
Rate limited
How to diagnose:
- Open the Network tab and filter by
/api/sdk/events - If you see
429status codes, you're being rate limited (600 requests/minute per tenant)
How to fix:
This typically only happens during initial rollout on high-traffic sites. The SDK automatically retries with exponential backoff. To reduce request volume:
- Check if you have the SDK installed on multiple high-traffic pages — consider a phased rollout
- If you're sending custom events at high frequency, batch them or reduce frequency
- Contact Break Ground support to discuss a rate limit increase if your traffic legitimately needs it
Plan limits reached
How to diagnose:
- Go to your Break Ground dashboard → Settings > Plan
- Check your Monthly Active Users (MAU) usage — if you're at 100%, new events are rejected
- Check if specific features (custom events, auto-tracking) show as unavailable on your plan
How to fix:
- Upgrade your plan in Settings > Plan to increase your MAU limit
- Or, if you're in a trial period, contact sales to extend the trial
- Events will start flowing again immediately once the limit is raised — no code changes needed
Auto-tracking disabled
How to diagnose:
- Check your SDK init config — if
autoTrackis set tofalse, the SDK only sendsSDK_INITand won't automatically capture page views or clicks - In the Console with
debug: true, you won't see auto-track event logs
How to fix:
Either remove autoTrack: false from your config (auto-tracking is enabled by default) or explicitly set it to true:
DAP("init", {
tenantKey: "wfx_...",
baseUrl: "...",
autoTrack: true, // default — remove autoTrack: false if present
});
If you intentionally disabled auto-tracking, you need to send events manually for Step 3 to pass:
DAP("track", "my_custom_event", { key: "value" });
Page navigation before event flush
How to diagnose:
This is most common on single-page sites where the user clicks a link immediately after the page loads. The SDK batches events and flushes them periodically — if the user navigates away before the first flush, events are lost.
How to fix:
- This usually resolves itself — the verification will pass once a user stays on a page long enough for the batch to flush (a few seconds)
- For testing, stay on the page for at least 5 seconds after it loads, then navigate
- If you're running a single-page app (SPA), this is less of an issue since navigation doesn't cause a full page unload
Verification timed out (after 2 minutes)
The dashboard polls the installation status every 5 seconds for up to 2 minutes.
Caching delay
The status endpoint caches results for 10 seconds. If you just deployed the SDK, events may not appear immediately.
How to fix: Click Check Again in the verification panel. Each click starts a fresh 2-minute polling cycle.
Event processing delay
Events are processed asynchronously through a background worker queue. Under heavy load, there can be a delay between the SDK sending events and them appearing in verification.
How to fix:
- Wait 30 seconds after your page loads, then click Check Again
- If it still times out after multiple attempts, the issue is likely one of the problems described in the sections above — follow the debugging checklist at the bottom of this page
Browser tab backgrounded
Some browsers throttle network requests and timers for background tabs. If you switch away from the dashboard tab during verification, polling may be delayed.
How to fix: Keep the dashboard tab in the foreground during the 2-minute verification check. Open your website in a separate browser window (not just a new tab) so both are visible.
Works locally but not in production
Localhost bypass
The API always allows requests from localhost (localhost, 127.0.0.1, *.localhost) regardless of the site domain configured on the API key. This means verification passes in local development even if the domain is wrong.
How to fix:
- Go to Settings > Sites and verify the domain matches your production URL exactly
- Common mistakes:
Site domain set to Production URL Result example.comwww.example.comFails (enable Allow Subdomains) staging.example.comapp.example.comFails (wrong domain) localhost:3000app.example.comFails (localhost only) - Enable Allow Subdomains if you serve from multiple subdomains
Different CSP policies
Your local dev server typically has no Content Security Policy, while production may enforce strict rules.
How to fix:
- Check your production site's CSP by opening DevTools → Console and looking for CSP violation errors
- Add the required domains to your CSP (see the "CSP blocking" section above):
script-src 'self' https://cdn.breakground.io;connect-src 'self' https://api.yourapp.com;
- To test CSP locally before deploying, add a CSP meta tag to your local HTML:
<metahttp-equiv="Content-Security-Policy"content="script-src 'self' https://cdn.breakground.io; connect-src 'self' https://api.yourapp.com;"/>
CDN or reverse proxy stripping headers
The API uses the Origin and Referer headers for domain validation. Some CDNs (Cloudflare, AWS CloudFront) or reverse proxies (nginx, Traefik) strip these headers.
How to fix:
- Check if your CDN/proxy is stripping the
Originheader:# From your production server, make a request that mimics the browsercurl -H "Origin: https://www.example.com" -H "X-API-Key: wfx_..." \https://your-api-domain/api/sdk/config -v 2>&1 | grep -i "origin" - Configure your CDN/proxy to forward the
Originheader:- Cloudflare: Origin is forwarded by default — no action needed
- AWS CloudFront: Add
Originto the cache policy's allowed headers - nginx: Add
proxy_set_header Origin $http_origin;to your proxy config - Vercel/Netlify: These forward headers by default — no action needed
Firewall or WAF blocking API requests
How to diagnose:
- Open DevTools → Network tab on your production site
- Filter by
/api/sdk/— look for blocked or failed requests - If requests aren't even appearing, the WAF may be blocking them before they leave the browser
How to fix:
- Allowlist the Break Ground API domain in your WAF/firewall rules:
- Outbound HTTPS (port 443) to your API domain (e.g.,
api.breakground.io)
- Outbound HTTPS (port 443) to your API domain (e.g.,
- If using a corporate proxy, ensure it doesn't block or modify requests to the API domain
- Test from behind the same network as your users:
If this times out or returns an unexpected response, the network is blocking itcurl -H "X-API-Key: wfx_..." https://your-api-domain/api/sdk/config
General debugging checklist
Follow these steps in order. Most issues are caught by steps 1–3.
Step 1: Enable debug mode
Add debug: true to your SDK config — this logs every SDK action to the browser console:
DAP("init", { tenantKey: "wfx_...", baseUrl: "...", debug: true });
Step 2: Check the Network tab
Open your website → press F12 → go to the Network tab → filter by /api/sdk/:
| Status | Meaning | Fix |
|---|---|---|
| No requests at all | SDK script didn't load or init wasn't called | See "Script not loaded" and "SDK not initialized" sections |
401 | Invalid API key, revoked key, or domain mismatch | Check the response body for the specific error code |
429 | Rate limited (>600 req/min) | Reduce traffic or contact support |
(failed) / network error | API endpoint unreachable | Check DNS, firewall, and API status |
| CORS error in Console | CSP or CORS misconfiguration | See "CSP blocking" section |
200 | Working correctly | Wait for verification to catch up (caching delay) |
Step 3: Check the Console tab
Look for:
[DAP]prefixed messages (SDK debug logs) — these show what the SDK is doing- Red CSP violation errors — these tell you exactly which resource was blocked and which directive to update
DAP is not defined— the SDK script didn't load
Step 4: Verify your API key
- Go to dashboard → Settings > API Keys
- Confirm the key in your snippet appears in the list and shows Active
- Check the Site column — if it's bound to a site, verify the domain matches
Step 5: Verify your site domain
- Go to dashboard → Settings > Sites
- Confirm the domain matches your production URL (including
www.if applicable) - Toggle Allow Subdomains if you serve from multiple subdomains
Step 6: Test API connectivity directly
Run this from your terminal (replace the values):
curl -H "X-API-Key: wfx_your_key_here" https://your-api-domain/api/sdk/config
| Result | Meaning |
|---|---|
| JSON response with tenant config | API key and endpoint are working — issue is browser-side (CSP, ad-blocker, script loading) |
{"error": "Unauthorized", "code": "AUTH_INVALID_API_KEY"} | API key is wrong or revoked |
{"error": "Unauthorized", "code": "AUTH_ORIGIN_MISMATCH"} | This won't happen in curl (no Origin header) — but confirms the key exists |
{"error": "Unauthorized", "code": "AUTH_TENANT_INACTIVE"} | Account suspended — contact support or check billing |
| Connection timeout / DNS error | API domain is wrong or blocked by your network |
Step 7: Check your plan limits
Go to dashboard → Settings > Plan and verify your MAU usage is below the limit.