API 401 / 403 / 429
401 means no key was sent or the key is wrong, 403 means the key lacks the required scope, and 429 means too many requests. The JSON code is more specific than the status.
Symptoms
401with codeNO_API_KEYorBAD_API_KEY.403with codeSCOPE_FORBIDDEN.429with codeRATE_LIMITandretryAfter.
Check in this order
- Read
codeandmessagein the body. Do not stop at the status. NO_API_KEY: server events and log import need the key inAuthorization: BearerorX-Api-Key. A missing header, a key in the query string, or an empty Bearer token all land here.BAD_API_KEY: the key does not exist, was revoked, has expired, or is not bound to a project. The full key is shown once at creation and starts withwck_.SCOPE_FORBIDDEN: the key is valid but lacks the scope this endpoint needs:track:writefor behavior events,events:writefor bot access records,logs:writefor log import.429: waitretryAfterseconds. Server behavior events allow 600 requests per minute per key, with up to 1,000 events per request.
Why
401 happens before the server recognizes the caller; 403 happens when the caller is recognized but this key may not call the endpoint; 429 happens when the rate window is full. Browser collection does not use these limits: excess events are dropped and still return 202, see SDK network and retries.
Fix
- Create a key with the right scope in ConsoleConfigurationSettingsAPI keys. Keep the full key in server environment variables only, never in front-end code.
- If the scope is wrong, change the key's scopes or use another key. Retrying never turns a 403 into a 200.
- On 429, back off for
retryAfterand put many events in one request'seventsarray instead of one request per event. - Keys and scopes: Authentication. Error shape: Errors. Limits: Rate limits.
Verify
Call the server events endpoint without Authorization and expect 401 / NO_API_KEY. With a track:write key in debug mode, the response shows validation results and nothing is stored.
Still failing?
Contact Contact support with the status, code, message, request path and method, the first 8 characters of the key and its name. Never paste the full key or the raw Authorization header.
SDK network and retries
When the collector returns 202, a queued SDK removes that batch, even if the server then decides not to store the events.
Symptoms
- After a few minutes offline, mini program or React Native events catch up, and a website pageview from a closed tab does not.
- The debug log says
switch endpoint → …, or the error callback getssend_failedwithHTTP <status>, batch of <count> dropped. - Killing the Android process loses events that were only in memory.
Check in this order
- Which SDK this is.
w.jspreferssendBeacon, thenfetchwithkeepaliveand no credentials. It has no local queue. A hit that did not leave before the page closed is gone. The first pageview uses afetchthat can read the response, so the script can load site config. - Mini programs, HarmonyOS, React Native, and uni-app share the JavaScript core: a local queue of at most 10,000 events, kept 7 days, about 1 MB.
429,5xx, and network errors (status 0) back off from 1 second, doubling, capped at 300 seconds. With more than one collect host, 10 failures in a row switch to the next host. - Any other
4xx(not 429): the core treats the batch as something that will not heal, deletes it, and recordssend_failed. A bad key, a wrong path, or a bad JSON body is this case. - Android and iOS 0.1.0 keep the queue in memory only, with no backoff. They retry on the next send. When the process ends, the queue ends. They also do not read remote config.
- The collect host is a domain only. The SDK appends
/api/v1/pulse. A path you add yourself hits the wrong URL, returns a 404-class 4xx, and the queue drops that batch. - The collector still returns
202for a bad domain, an exhausted quota, or an exclusion rule. The SDK treats 2xx as ack and does not retry. If the report is empty, do not hunt the retry log. Use No data showing up.
Causes
The website script stays small, so it does not queue in the browser. SDKs with storage keep failures that can recover (rate limit, server error, offline) and drop 4xx failures that cannot, so one bad batch does not block the queue indefinitely. 202 counts as success on this path because the collector uses it for "I finished handling this", including "I chose not to store it".
Fix
- A website pageview lost as the tab closed cannot be retried. Confirm the request left while the page was still open. Blockers and CSP: CSP, blockers & networks.
- Use the host shown on the console SDK code page. Do not append
/api/v1/pulseyourself. - On a 4xx that is not 429, read the body, fix the key, path, or fields, and send a new event. The old batch is already acked.
- On 429, let the queue back off. Do not also fire the same batch through a path that skips the queue.
- Capability matrix: SDK overview. Browser collect protocol: Collect (browser).
Verify
On a mini program or React Native build, send one event while offline, then restore the network. The queue should flush after backoff and realtime should show it. Point the collect host at a path that returns 404. That batch should be dropped, not retried again. The website script should show one sendBeacon or fetch in the network panel, with no automatic second try.
Still failing?
Use Contact support with the package name and version, the collect host (no key), the HTTP status, whether several hosts are configured, and roughly how many events are still queued. Do not paste a full API key.
Chat widget not showing
Chat and analytics share w.js. The bubble appears only after the first pageview response tells the script to load chat.
Symptoms
- No bubble, and the network panel never requests
w-chat.js. w-chat.jsloads and the bubble is still missing, especially on a phone.- The bubble is there, and the first visitor message turns into the offline form.
- The chat window says the origin is not allowed.
Check in this order
- ConsoleLive chatChat settingsAppearance has Enable live chat saved. Only the owner and admins can change it.
- The page was reloaded. An already open page does not fetch
w-chat.json its own. The script waits for the first pageview response and lazy-loadsw-chat.jsfrom the same directory only when that JSON says chat is on. - The tag does not set
data-chat="false".data-chat="true"skips the wait for the first pageview response. If the switch is still off, the bubble stays hidden. - Analytics itself is collecting. Without a pageview response the script never learns to load chat. Confirm collect with No data showing up.
- The current domain is on the allowed-domain list. Chat uses the same list as analytics. See Project settings.
- On a viewport under 640 pixels, or a browser treated as mobile, Hide bubble on mobile hides the bubble even when the script has loaded.
- This month's conversation quota, summed across every project owned by the site owner. At the cap, with metered overage off, new conversations are rejected and the visitor sees the offline form. The bubble can still be visible. See Billing & quotas.
- Whether Content Security Policy blocks
w-chat.js, the chat gateway, or the page inside the chat window. See CSP, blockers & networks.
Causes
w.js does not add a second chat script in the HTML. The first pageview's JSON config decides whether to load w-chat.js. The switch, the domain, and the mobile hide option decide whether a bubble appears. Quota decides whether a new conversation can be created.
Fix
Turn the switch on and reload, as on Install & enable. To skip one page, set data-chat="false" on that tag. Do not turn the project switch off for a single page. The standalone page https://app.tapcub.com/c/SITE_KEY does not need the website snippet. Use it to separate "the site never loaded the script" from "chat itself is down".
Other chat symptoms (messages, notifications, translation, offline) are on Live chat troubleshooting.
Verify
Reload a page on a registered domain. The network panel shows w-chat.js and a bubble appears in the corner. Send a test message. ConsoleLive chatInbox shows it under Waiting. When an agent is online, the bubble shows the online marker.
Still failing?
Use Contact support with whether the switch was saved, whether w.js and w-chat.js appear in the network panel, the tag's data-chat value, whether the viewport is a phone width, and any domain or quota notice inside the window. Do not paste a full API key.
AI assistant issues
The assistant is at ConsoleOperationsAI assistant. It parses with rules first and calls the platform model only when the rules are not confident enough.
Symptoms
- The question comes back as a request to rephrase, or as a prompt for funnel steps.
- Yesterday "visitors in the last 7 days" returned a number. Today the same question stays on a narrow rule result and is no longer rewritten.
- The assistant names an event that is not in the event list.
- An MCP
askcall returns 401 or 429.
Check in this order
- The question is not empty and is at most 500 characters. On the page, an empty or longer question is
400withcodeVALIDATION. MCPaskcuts the text at 500 characters instead. - The rules can match a metric, an event name, and a time range. Rules use event names this project has already received. An event that was never sent becomes a clarification, not an invented number.
- The platform has a model enabled and a key configured. Without that, the assistant stays on the rule path. High-confidence questions still return numbers. Low-confidence questions return a clarification and no model rewrite.
- This project's model calls for the day are under the cap. The default is 2,000 calls per project per calendar day in the project time zone (an operator can change it). Over the cap, that call skips the model and returns the rule result. If the counter itself fails, the request still uses rules. The page does not become an error.
- Figures in the answer come from the query that just ran. The model may rewrite the rule-based sentences. It is not given permission to add numbers that are not in the result. If the report disagrees, compare the range and the counting mode first. See Dates and time zones and Counting modes.
- The MCP endpoint is
/api/v1/mcpand uses the same session or API key as the rest of the API. Treat 401, 403, and 429 with API 401 / 403 / 429. They are not "the assistant did not understand".
Causes
The two paths are separate on purpose. The rule path does not need an external model, so a model outage, a missing key, or a used-up daily cap still leaves a number on questions the rules can parse. The model path runs when rule confidence is below 0.9, when no query was produced, or when the conversation already has history, and it spends the daily cap first. If the query fails to run, the API still returns and puts the reason in the clarification field.
Fix
- Ask in report language: a range, a metric, and an optional breakdown. For example, "visitors in the last 7 days, by channel". A funnel needs at least two steps.
- Use event names that have already been stored. See Events & properties. A spoken alias is not an event name.
- When the model is off, stay with questions the rules can answer, or ask an operator to enable the platform model. That switch is not on the project plan page.
- When the daily cap is spent, wait until the next day in the project time zone, or ask an operator to raise the cap. The feature page is AI assistant.
Verify
Ask for visitors in the last 7 days. The rules should return one number for that range in the project time zone. Ask for an event name that does not exist. You should get a clarification, not a new number. With a model enabled, the headline can be shorter than the rule draft, and the figures still match the query result.
Still failing?
Use Contact support with the question text, the clarification or headline you got back, the approximate time, and whether you used the page or MCP. Do not paste a full API key or a model-provider key.