Verify and debug
Confirm an install works, read the server's receipt and refusals, and find out why events or flags are missing.
Use this page to confirm that events reach MIRA FIVE, and to find out why they do not. Every check below works without the dashboard except the last step, the live view. For the codes themselves, see Errors.
Check an install
In the browser:
- Open the site on its real domain. From
localhost, nothing is sent unless you allow it (see Localhost). - Open the browser's developer tools on the Network tab.
- Send the queue now instead of waiting up to 5 seconds: run
mirafive('flush')in the console for the script tag, or callmira.flush()in your code. - Find
POST https://events.mirafive.io/v1/batch/mf_…. It answers202with a receipt:
{ "batch": "0192d4a8-7b1c-4e8a-9c1d-2b3e4f5a6b7c", "accepted": 1, "dropped": 0 }- Open the source's live view in MIRA FIVE and find the event.
| Receipt field | Meaning |
|---|---|
batch | The batch id the SDK sent |
accepted | Events stored |
dropped | Events not stored |
reason | Present when nothing was kept: bot, install_check, ingestion_paused or allowance_exhausted |
A 202 is final, also when it kept nothing. The reasons:
| Reason | Cause and fix |
|---|---|
bot | A website key received a batch from a bot user agent: a crawler, a headless or automated browser (Playwright, Puppeteer, Selenium, Lighthouse), or a request without a user agent. Test with a normal browser. |
install_check | The batch held only $install_check events. Expected, see Install check. |
ingestion_paused | Your organization's ingestion is paused. |
allowance_exhausted | The plan's monthly event allowance is used up. |
For a server, send an install check and read the receipt, or pass onError and watch it.
Localhost
Browser SDKs send nothing from localhost, 127.*, [::1], *.local or a file: page, so development does not pollute your numbers. To send from there:
<script defer src="https://cdn.mirafive.io/mira.js" data-key="mf_…" data-track-localhost></script>The Astro integration is also off in astro dev unless you pass dev: true (see Astro).
The server always accepts localhost and 127.0.0.1 as origins, so you do not add them to a source's allowed origins. Use a separate source for development if you want to keep test events apart.
Install check
$install_check is an event that proves a key and a host work. The server answers it with a receipt and never stores or bills it:
curl https://events.mirafive.io/v1/batch \
-H "Authorization: Bearer $MIRAFIVE_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{"v":1,"batch":"0192d4a8-7b1c-4e8a-9c1d-2b3e4f5a6b7c","mode":"full","events":[{"name":"$install_check"}]}'The answer:
{ "batch": "0192d4a8-7b1c-4e8a-9c1d-2b3e4f5a6b7c", "accepted": 0, "dropped": 1, "reason": "install_check" }mirafive:check prints the host, the key's namespace and the mode, sends the check and fails unless the reason is install_check. The install check runs before the bot filter, so curl works with a website key too: POST /v1/batch/mf_… with the same body and no Authorization header.
Refusals
A refused batch answers with an error body, { "code": "…", "detail": "…" }:
| Status and code | Cause | Fix |
|---|---|---|
401 unauthorized | The key is missing, unknown, revoked or archived | Copy the key again from Keys. Servers read MIRAFIVE_SECRET_KEY |
403 origin_not_allowed | The page's origin is not in the source's allowed origins | Add the site's domain to the source. www. and the bare domain count as one site |
403 website_key_as_bearer | A website key was sent as Authorization: Bearer | Server code needs the secret key of a server source |
403 secret_key_in_path | A secret key was put in a browser URL. The key is now marked exposed | Browsers use the website key. Rotate the secret key |
403 secret_key_exposed | Flags only: a secret key arrived with browser headers | Rotate the key, and keep it out of browser code |
403 lookup_not_allowed | Flags only: a segment lookup on a consentless source | Use a full source, or no targeting consent in the page |
400 collection_mode_not_allowed | A full batch to a consentless source | Switch the source to full, or send consentless |
400 validation_failed | An event breaks a rule; errors names it, such as events.0.name | See event names and property limits |
400 invalid_json | The body is not valid JSON, often a string cut inside a character | Use an SDK, or send well-formed UTF-8 |
413 payload_too_large | The body is over 1 MiB | Send fewer events per batch |
429 rate_limited | Too many requests or events | Wait for Retry-After; the SDKs do |
The browser SDKs show the code as a development warning ([mirafive] batch: origin_not_allowed). MIRA FIVE also records refusals that come after the key check on the source, so you can see them there.
Nothing arrives
Work down this list:
- The page runs on
localhostor another local host withouttrackLocalhost(see Localhost). - Do Not Track or Global Privacy Control is on in your browser. The browser SDKs then send nothing, by design. Test in a profile without them.
window.__mirafive_ignoreis set, for example because you are signed in as an admin (see Ignore your own visits).- The page is in full mode and no statistics consent was given. Full mode sends nothing before the grant (see Consent).
- The page's Content Security Policy blocks the request. Allow
connect-src https://events.mirafive.io(or your host), and for the script tagscript-src https://cdn.mirafive.io. - The receipt says
droppedwith a reason, or the request is refused (see above). - Server events: the process ended before the buffer was sent (see Batching and flushing), or
onErrorreports a failure.
Development warnings
Browser SDKs log warnings starting with [mirafive] only on a local host name (localhost, 127.*, [::1], *.local, file:), once per message. To see warnings about the server's answers, develop on localhost with trackLocalhost: true.
| Warning | Meaning |
|---|---|
local host: set trackLocalhost | Nothing is sent from this host |
bad name: … | track() got an empty, too long or $ name |
event dropped: … | The event's properties break a limit, so it was dropped alone |
batch: … | The server refused the batch (the code) or kept nothing (the reason) |
batch dropped | A batch failed three times and was given up |
consent() needs its plugin (or another method) | The client lacks the plugin: identity(), flags(), siteSearch() |
identity() needs mode "full" | identity() is in a consentless client |
siteSearch() does nothing in mode "consentless" | Site search needs full mode |
identify() needs a user id of 1–256 characters | The user id is empty or too long |
identify(): the id looks like an email | Pass your internal id instead |
flag "…" is unknown | This source's flags have no such key |
second client stays inert | createMira() ran twice on one page; the second client does nothing |
The script tag logs these on every host, since they mean a broken install: [mirafive] no data-key, [mirafive] bad key (the data-key is not a website key), [mirafive] no script tag, and [mirafive] <feature> chunk failed (a part of the script was blocked by the network, a Content Security Policy or an integrity check).
createMira() throws a TypeError for a secretKey option, a host without a scheme, and mode: 'full' without identity(). Server SDKs report failures to onError, or log [mirafive] … without one.
Tests
Keep tests from sending events:
| SDK | In tests |
|---|---|
| Laravel | Mira::fake(), then assertTracked(), assertNotTracked(), assertIdentified(), assertNothingTracked(). See Laravel |
| Symfony | mirafive: { test: true } under when@test, then the InteractsWithMira trait and MiraFake. See Symfony |
| PHP | new Mira(enabled: false) sends nothing and needs no key, but still refuses bad input. See PHP |
@mirafive/sdk-server | Pass a fetch function to new Mira() and assert on the requests it receives. See Node.js and edge |
The fakes still check every event, so an event MIRA FIVE would refuse fails the test as it would fail in production.