Custom Events
Custom events are event names that YOU define for your workspace, such as
quote_requested, size_guide_opened or
show_hide_filter. They cover the actions your funnel has that the
standard events do not. A custom event flows through the whole AxiTrace pipeline:
ingestion, storage, the visitor profile timeline and, if you switch it on,
forwarding to your connected ad platforms.
AxiTrace accepts ONLY names that exist in your workspace's registry. A name you have not defined is rejected and shows up in your Errors tab (Event feed) and in the "Recently rejected names" list, where you can define it with one click.
On This Page
- Where to find Custom Events
- Availability and plan limits
- Quick start: a working event in five minutes
- The definition form, field by field
- Event key rules and reserved names
- Kinds: engagement and conversion
- Throttle modes
- Properties
- Sending from the website (JavaScript SDK)
- No-code triggers
- The selector tester
- Sending from your server (HTTP API)
- Sending from PHP (PHP SDK)
- Forwarding to ad platforms
- Checking that it works
- Editing, disabling and deleting
- How fast changes take effect
- Consent and privacy
- Limits at a glance
- Error messages
- Known limitations
- Troubleshooting
Where to Find Custom Events
Custom events are configured per workspace, in the Custom events module:
- Sign in at axitrace.com/admin and open your workspace.
- Click Custom events in the sidebar, in the Data group.
- The same Custom Events card also appears in Workspace settings → Data collection.
The card shows:
- how many custom events are active and how many your plan allows (for example "2 of 5 active");
- a table of your definitions with their kind, status, the number of events received in the last 7 days;
- the New custom event button;
- the Recently rejected names list: names your site sent in the last 7 days that are not defined, each with an Add as custom event button;
- a link back to this guide.
The full list, including deleted definitions, is at
/admin/workspaces/<workspace-id>/custom-events. The card links to it.
You can also let an AI assistant set custom events up for you, for example by asking Claude "Track clicks on the size guide button as a custom event": see Connect Claude, ChatGPT and Cursor. Scripts can do the same through the REST API v1. Both follow the rules on this page and need a permission you grant separately.
Availability and Plan Limits
The plan limit counts ACTIVE definitions. Disabled and deleted definitions do not count, so you can disable one event to make room for another.
| Plan | Active custom events |
|---|---|
| Free | Not available |
| Legacy Starter | Not available |
| Pro | 5 |
| Business | 10 |
| Custom | Unlimited |
Custom event activity is shown as a technical diagnostic. Your plan allowance is measured in visits: multiple custom events in the same session do not create extra billable visits. Review unusually frequent events when tuning triggers and throttles.
Quick Start: a Working Event in Five Minutes
Example: count how often visitors open and close the product filters on a catalog page. The button in the page looks like this:
<button id="toggleFilters" type="button">
<span id="filterToggleText">Hide Filters</span>
</button>
- Check the SDK. The AxiTrace JavaScript SDK must already be installed on the site (see JavaScript SDK). No code change is needed for this example.
- Open the module. Workspace, Custom events in the sidebar, then New custom event.
-
Fill in the details.
- Event key:
show_hide_filter - Display name:
Show/Hide filters - Kind: Engagement (no value)
- Throttle: Unlimited (every click counts)
- Event key:
-
Add a trigger. In No-code triggers click Add trigger,
choose Click and enter the CSS selector
#toggleFilters. Use the button, not the innerspan: a click trigger matches the element you name AND everything inside it. - Leave destinations off and click Create custom event.
-
Test without sending. Open your page with
?axitrace_test=1added to the URL. The selector tester showsclick: #toggleFilterswith a match count of 1, and hovering that row outlines the button. Click the button: the "Would fire" log showscustom show_hide_filter (trigger). Nothing is sent from that tab. - Test for real. Open the page in a NEW browser session (a private window is easiest, because the website SDK picks up new definitions when a session starts) and click the button.
- Find the event. Open the Visitors tab, open your own visitor profile and look at the timeline: the event appears with an Engagement badge and the display name Show/Hide filters. The 7-day count on the card grows too.
The Definition Form, Field by Field
Event details
| Field | Required | What it does |
|---|---|---|
| Event key | Yes |
The technical name your code and triggers send, for example quote_requested.
See Event key rules. It cannot be changed after the event is created.
|
| Display name | Yes | The human name shown on the card and on visitor profiles. Up to 80 characters. You can change it any time. |
| Description | No | A note for your team about what the event means. |
| Kind | Yes | Engagement (no value) or Conversion (optional value). See Kinds. |
| Throttle | No | How often the event may fire. Once per page view, Once per session, Every N seconds or Unlimited. See Throttle modes. |
| Seconds between events | Only for Every N seconds | A whole number from 1 to 3600. |
Conversion fallback
Two optional fields for conversion events: Value and
Currency (a 3-letter ISO 4217 code such as USD or EUR).
They are used ONLY when an event arrives without its own value or currency. Typical use:
a "demo booked" conversion that is always worth the same amount, so your code or a trigger
never has to send a value.
Declared properties
Up to 10 rows. Each row has a Property name (same rules as the event key), a Type (String, Number or Boolean) and a Label. Declared properties are the ONLY properties forwarded to ad platforms. See Properties.
No-code triggers
Up to 5 rows. Each row has Fires on (Click, Hover or Visible), a CSS selector (up to 300 characters, validated when you save), Hover delay (ms) and Visible for (ms). Under each trigger, Send a property can fill one declared property from the matched element. See No-code triggers.
Destinations
One block per ad platform. Every destination is OFF until you switch it on, and a platform that is not connected in Connections shows a Connect button instead of a switch. See Forwarding to ad platforms.
The side panel of the form shows a ready-to-copy code snippet for the event and, when your workspace has a verified domain, a link that opens your site in the selector tester.
Event Key Rules and Reserved Names
An event key must:
- start with a lowercase letter;
- contain only lowercase letters
a-z, digits0-9and underscores_; - be 1 to 40 characters long.
^[a-z][a-z0-9_]{0,39}$
| Key | Valid? | Why |
|---|---|---|
quote_requested | Yes | |
video_played_75 | Yes | |
QuoteRequested | No | Uppercase letters |
quote-requested | No | Hyphen |
quote.requested | No | Dot |
1st_visit_popup | No | Starts with a digit |
purchase | No | Reserved name |
ga_menu_click | No | Reserved prefix |
Reserved names
You cannot use a key that AxiTrace or GA4 already uses:
-
Reserved prefixes:
ga_,google_,firebase_,axi_,vt_and a leading underscore. -
AxiTrace standard events and their aliases, for example
page_view,product_view,view_item,add_to_cart,begin_checkout,add_payment_info,purchase,transaction,form_submit,lead,subscribe,sign_up,start_trial,search,identify,engagementanderror. For these actions use the standard events, which already map to every platform. -
GA4 reserved and automatically collected names, for example
session_start,first_visit,user_engagement,screen_view,login,share,generate_lead,refund,view_cart,view_item_list,select_item,remove_from_cartandadd_shipping_info.
The form tells you when a key is reserved, so you do not need to memorise the list.
The key cannot be edited after creation, and a key stays taken by its definition even after you delete it: the form refuses it with "belonged to a deleted custom event and cannot be reused". A key held by a disabled event is refused too; enable that event instead. Pick a key you are happy to keep. The display name is the part you can change freely.
Kinds: Engagement and Conversion
| Kind | Value | Use it for | Behaviour |
|---|---|---|---|
engagement |
Never | Interest signals: filter toggles, video plays, size guide opens, tab switches. | Never carries a value and never counts as revenue. A value sent by the website SDK is ignored; a value sent to the HTTP API is rejected with a 400. Default throttle: once per page view. Google Ads and LinkedIn cannot receive engagement events. |
conversion |
Optional | Business outcomes: quote requested, demo booked, catalog downloaded, call clicked. |
May carry value, currency and transaction_id, with
the conversion fallback applied when they are missing. Goes through the same retry and
enrichment steps as the standard conversion events. Default throttle: unlimited.
|
Throttle Modes
The throttle stops a single visitor from sending the same event too often, for example when a visitor hovers over the same element again and again.
| Mode | Website SDK | HTTP API and PHP SDK |
|---|---|---|
Once per page viewonce_per_page_view |
At most one event per page view. A single-page-app navigation or a back/forward page restore counts as a new page view. | Not enforced: servers have no page views, so every request is accepted. |
Once per sessiononce_per_session |
At most one event per AxiTrace session. | At most one event per session_id (or user_id when there is no session) within 30 minutes. Requests without either are not throttled. |
Every N secondsevery_n_seconds |
At least N seconds (1 to 3600) since the last event that was actually SENT. | At least N seconds per session_id or user_id. |
Unlimitedunlimited |
Every call is sent, with a safety cap of 50 events per event key per page view. | Every request is accepted, subject to the workspace rate limit. |
A throttled call in the browser is dropped silently. A throttled HTTP API request is answered
202 with {"status": "throttled"}.
Properties
Properties are extra details sent with an event, for example the name of the menu a visitor opened. There are two layers:
- What you may SEND. Up to 30 properties per event. Names are 1 to 40 characters of letters, digits and underscores. Values must be a string, a number, a boolean or null. Strings are limited to 500 characters. Objects and arrays are not allowed.
- What is FORWARDED. Only the properties you declared on the definition (up to 10) are sent to ad platforms, converted to the declared type: a Number property is sent as a number, a Boolean as true or false, a String as text. Undeclared properties are stored with the event but never leave AxiTrace.
The conversion never guesses. A Number property receives the number when the value reads as one,
including the numeric text "34" a trigger extracts from an attribute; anything else
is forwarded as 0, so a Number property holding a price range such as
"2000-5000" reaches every platform as zero. Use a String property for a value that is
not a plain number, and check the type when a platform reports zeros.
Some names are reserved for the event itself and are never treated as properties:
value, currency, transaction_id, trigger_id,
source, url and referrer. Pass value, currency and
transaction id through the dedicated options described below.
Do not put e-mail addresses, phone numbers, names or other personal data into properties. Properties are forwarded to ad platforms as plain values. Identity is matched through AxiTrace's own visitor identification, not through properties.
Sending From the Website (JavaScript SDK)
With the JavaScript SDK installed, one call sends a custom event. The signature is:
Axitrace._instance.track(eventKey, properties, options);
| Argument | Type | Description |
|---|---|---|
eventKey |
string | The event key exactly as defined, for example 'quote_requested'. |
properties |
object, optional | Flat key/value pairs. See Properties. |
options.value |
number, optional | Conversion events only. Zero or more. |
options.currency |
string, optional | Conversion events only. ISO 4217 code, uppercased automatically. |
options.transaction_id |
string, optional | Conversion events only. Your own order or lead id, up to 100 characters. |
options.products |
array, optional | Products the event is about, in the transaction line shape, shown when the event counts as a lead (see Custom events as leads). Not a property: it does not count toward the 30 properties. |
Engagement example
document.querySelector('#size-guide').addEventListener('click', function () {
Axitrace._instance.track('size_guide_opened', {
product_sku: 'CHAIR-042',
placement: 'product_page'
});
});
Conversion example
Axitrace._instance.track('quote_requested', { menu_name: 'Salad' }, {
value: 149.50,
currency: 'PLN',
transaction_id: 'quote-1001'
});
What the SDK does with the call
- Defined and active key: applies the throttle, cleans the properties, attaches the conversion fields, fires the browser pixel bridge (see Meta and TikTok) and sends the event.
- Invalid property: a property with an invalid name, a non-scalar value or beyond the 30th is dropped with a console warning; the rest of the event is still sent. Strings longer than 500 characters are cut to 500.
- Unknown key: the SDK logs
"<key>" is not a registered custom eventin the console and sends the name ONCE per session, so it appears in your Errors tab and in "Recently rejected names" without flooding them. - Key of a standard event: names such as
add_to_cartare routed to the standard event, not to a custom event. - Before consent: see Consent and privacy.
_instance
Tracking calls go through Axitrace._instance, the same object every other event page
in these docs uses. Call it after Axitrace.init() has run. Since SDK 0.23.0 the global also
accepts the shorthand Axitrace.track(...) (and Axitrace.trackCustom(...)), which
forwards to Axitrace._instance and returns its result; a shorthand call made before
init() is kept and replayed once it runs.
No-Code Triggers
A trigger fires the event when a visitor interacts with an element that matches a CSS selector. No code on your site is needed beyond the SDK itself. A definition can have up to 5 triggers, so one event can be fired by several buttons.
| Fires on | Behaviour | Timing field |
|---|---|---|
| Click | Fires when the visitor clicks the matching element or anything inside it. The click itself is never blocked or changed, so links and buttons keep working. | None |
| Hover | Fires when the pointer stays on the element for the hover delay. Leaving earlier cancels it. Not available on touch-only devices such as phones, where hover does not exist. | Hover delay (ms): 100 to 5000, default 700 |
| Visible | Fires when at least half of the element has been on screen for the dwell time in total (it may scroll in and out). Fires at most once per element per page view. | Visible for (ms): 100 to 60000, default 1000 |
Choosing a selector
- Prefer an id:
#toggleFilters. - Next best is a dedicated data attribute:
button[data-track="quote"]. Adding one to your template is the most robust option, because designers rarely change it. - Class names work, but break when styling changes:
.product-card .btn-quote. - A selector may match several elements. For example
.faq-item summaryfires for every FAQ question. - Elements added to the page later, such as items loaded by infinite scroll or a popup, are picked up automatically.
What triggers never do
- Triggers never read the text of an element and never read form field values, so they cannot capture what a visitor types.
- Triggers are not loaded at all while your workspace has no trigger defined, so they add nothing to your page weight.
The first one is a deliberate boundary, not a missing feature. A trigger is configured in a panel
by whoever runs your marketing, and a selector such as .checkout input matches the
fields a visitor types into. If triggers could read text and input values, one selector would be
enough to push an e-mail address, a phone number or a full name into your ad platforms, with no
code review and no way to take it back. Everything else in AxiTrace works to keep personal data
out of forwarding, so triggers do not get to be the way around it. Values that a trigger may read
are the ones your own template put there on purpose, as attributes.
Sending a property from a trigger
Under each trigger row, Send a property fills ONE declared property from the element the trigger matched. Declare the property first under Declared properties.
Be aware of what this costs. A trigger fires on its own against the page as it already is, but a
property has to come from somewhere, and the only places a trigger looks are the link address and
data- attributes. Unless those attributes are already in your markup, someone has to
add them to your theme: one attribute per element, no script and no tracking code, but still an
edit to the template. A trigger with no property needs nothing at all.
| Taken from | Data attribute | Reads |
|---|---|---|
| A data attribute of the element | Required, without the data- prefix |
The attribute of the matched element itself. |
| The link address (href) | Not used | The href of the matched element. |
| A data attribute of a parent | Required, without the data- prefix |
The attribute of the nearest element, the match itself or one of its parents, that carries it. Useful when the click lands on a button inside a product card. |
Example: one trigger for every "Request a quote" button in a menu list.
<li data-menu-name="Salad">
<button class="btn-quote">Request a quote</button>
</li>
- Declared property:
menu_name, type String. - Trigger: Click, selector
.btn-quote. - Send a property: A data attribute of a parent, data attribute
menu-name, into declared propertymenu_name.
A click on the button sends menu_name: "Salad". When the attribute is missing or empty, the event
is still sent, without that property. Values longer than 500 characters are cut. To send more than one
property, call track() from your code.
The trigger engine (axitrack-triggers.js) and the selector tester
(axitrack-tester.js) are loaded on demand from the same tracking domain as the SDK.
If your site uses a Content Security Policy that already allows the SDK, nothing else needs to be added.
Throttle and triggers
The throttle applies to trigger fires exactly like it applies to code calls. A Hover or Visible trigger on an element visitors see often should normally use Once per page view or Every N seconds.
The Selector Tester
Add ?axitrace_test=1 to any page of your own site, for example
https://www.example.com/catalog?axitrace_test=1. The definition form links to your
verified domain with the flag already set.
In that browser tab:
- Nothing is sent to AxiTrace. The tab runs in dry-run mode, so testing never pollutes your data.
- A panel in the top-right corner lists every trigger as
type: selector (event: key)with the number of elements it matches on this page.0means the selector finds nothing here;invalidmeans the selector has a syntax error. - Hovering a row outlines the matching elements in orange.
- Your triggers are armed. The Would fire log shows every event that would have been sent, with its properties, instead of sending it: trigger fires are marked
(trigger), and calls from your own code appear too. - Clicking any element on the page proposes a selector for it: its id first, then a data attribute, then a short class path. Copy it into the trigger row.
The tester uses the definitions your browser session already loaded. After you save a change, open the test URL in a new private window to see it.
Sending From Your Server (HTTP API)
Use the HTTP API when the action happens on your server: a quote stored in your CRM, a
subscription renewed by a webhook, a phone call logged by your call-tracking tool.
Send requests to https://stat.axitrace.com or to your own tracking subdomain.
Authentication
Use HTTP Basic authentication with your SECRET key as the username and an empty password, exactly like the other server endpoints. See Authentication. Never put the secret key in browser code.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
event_name | string | Yes | The event key. |
event_id | string | Yes | Your unique id for this occurrence, for example a UUID. Used for deduplication. |
properties | object | No | Flat key/value pairs. See Properties. |
value | number | No | Conversion events only. Zero or more. |
currency | string | No | Three uppercase letters (ISO 4217), for example EUR. |
transaction_id | string | No | Up to 100 characters. |
products | array | No | Products the event is about, in the transaction line shape. An invalid line is dropped and the event is still stored. See Custom events as leads. |
client_id | string | Recommended | The value of the visitor's vt_vid cookie. Links the event to the visitor profile and to their ad clicks. |
session_id | string | Recommended | The value of the vt_sid cookie. Needed for session-based throttling. |
user_id | string | No | Your own customer id. Takes precedence over client_id as the visitor identity. |
ip | string | Recommended | The visitor's IP address. Without it, the IP of your server is used, which weakens ad platform matching. |
user_agent | string | Recommended | The visitor's browser user agent. Without it, the user agent of your HTTP client is used. |
Example
curl -X POST https://stat.axitrace.com/v1/custom-events \
-u "sk_live_YOUR_SECRET_KEY:" \
-H "Content-Type: application/json" \
-d '{
"event_name": "quote_requested",
"event_id": "5f0c7a52-8a4e-4a53-9a3e-0f1d2c3b4a51",
"properties": { "menu_name": "Salad", "guests": 12 },
"value": 149.50,
"currency": "PLN",
"transaction_id": "quote-1001",
"client_id": "value-of-the-vt_vid-cookie",
"session_id": "value-of-the-vt_sid-cookie",
"ip": "203.0.113.24",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ..."
}'
Responses
| Status | Body | Meaning |
|---|---|---|
200 | {"status": "queued"} | Accepted and queued for processing. |
202 | {"status": "duplicate"} | An event with the same event_id was received in the last 5 minutes. Nothing new was queued. |
202 | {"status": "throttled"} | The definition's throttle suppressed this event. |
400 | plain text error | Invalid request or unknown event. The message is also written to your Errors tab (Event feed). See Error messages. |
401 | plain text error | Missing or wrong secret key. |
429 | custom event budget exceeded | More than 600 custom events in one minute for this workspace. Retry in the next minute. |
500 | plain text error | Temporary failure on our side. Retry with the SAME event_id. |
/v1/custom-events is the only endpoint with event_id deduplication, so
retrying after a timeout or a 500 never double counts. An event_id is claimed
only by a request that passed validation: a request answered 400 claims nothing, so you
can fix it and resend it with the same id. A 500 releases the id as well.
Sending From PHP (PHP SDK)
The AxiTrace PHP SDK
provides the CustomEvent model from version 1.7.0. It sends to /v1/custom-events and
generates the event_id for you when you do not pass one.
use AxiTrace\Model\Event\CustomEvent;
$event = new CustomEvent(
'quote_requested', // event key
['menu_name' => 'Salad'], // properties
149.50, // value (conversion events only, or null)
'PLN', // currency, or null
'quote-1001', // transaction id, or null
$quote->getUuid() // event id, or null to generate one
);
// Link the event to the visitor who made the request.
$event->setClientId($_COOKIE['vt_vid'] ?? null);
$event->setSessionId($_COOKIE['vt_sid'] ?? '');
$axiTrace->events()->send($event);
Pass a stable event id, such as the id of the record you just stored, if the same code path can run twice. Then a repeated call within 5 minutes is answered as a duplicate instead of counting twice.
Custom events as leads
A registered custom event can count as a lead: switch it on in Settings > Leads and its
occurrences appear in Leads. Choosing it changes only what Leads shows,
never how the event is tracked or forwarded. Any custom event can carry the products it is about as a root
products array (in the JavaScript SDK, options.products), anchored on the event's
currency. Products are optional; an invalid line is dropped and the event is still stored. From
PHP SDK 1.12.0, CustomEvent has addProduct() and setProducts():
$event = (new CustomEvent('quote_requested', ['menu_name' => 'Salad'], 149.50, 'PLN', null, $quote->getUuid()))
->addProduct(['sku' => 'MENU-SALAD', 'name' => 'Salad menu', 'finalUnitPrice' => 14.95, 'quantity' => 10]);
$axiTrace->events()->send($event);
The full rules for lead products are on the Leads page.
Forwarding to Ad Platforms
General rules for every platform:
- Forwarding is configured per definition, in the Destinations section of the form. The per-event checkboxes of a connection in Connections do not apply to custom events.
- A destination can only be switched on when the platform is connected in Connections.
- Only declared properties are forwarded, converted to their declared type.
- For a conversion event, the conversion fallback value and currency fill in what the event does not carry.
- Names of purchase-type standard events (such as Meta
Purchaseor GA4purchase) cannot be used for custom events, so a custom event can never inflate your revenue reporting. - An engagement event that carries no identity signal at all (no cookie, no click id, no user id) is not forwarded, because the platform could not match it to a person. It is still stored and visible on the profile.
- Every forwarding attempt, successful or not, appears in the connection's integration log.
| Platform | Settings in the destination block | What you must do on the platform side |
|---|---|---|
| Meta (Facebook) Conversions API |
Send as: Custom event name (1 to 50 letters, digits, spaces and underscores; defaults to the key)
or Standard event: Lead, Contact, Schedule, SubmitApplication,
ViewContent, Search, CustomizeProduct, FindLocation,
Donate, CompleteRegistration, StartTrial, Subscribe.
|
Nothing for a standard event. A custom name appears in Events Manager after the first event; create a custom conversion from it if you want to optimise campaigns for it. If the Meta pixel runs on your site, the SDK also fires it with the same event id, so Meta counts the pair once. |
| TikTok Events API |
Send as: Custom event name (starts with a letter, 1 to 50 letters, digits, hyphens and underscores)
or Standard event: Contact, SubmitForm, ViewContent, Search,
ClickButton, Download, CompleteRegistration, Subscribe.
Optional Description property: one declared string property sent as the TikTok description.
|
Nothing for a standard event. If the TikTok pixel runs on your site, the SDK also fires it with the same event id for deduplication. |
| Google Analytics 4 | GA4 event name: starts with a letter, up to 40 letters, digits and underscores; defaults to the key. GA4 reserved names are refused. |
GA4 does not show event parameters in reports until you register them. In GA4 go to Admin,
Custom definitions, Create custom dimension, scope Event, one per declared property,
plus value and currency for a conversion event. To count the event as a
key event, mark it in Admin, Events. GA4 sends value and currency together or not at all.
|
| Google Ads | Conversion events only. Conversion action ID: the numeric id of an existing conversion action in the connected account. | Create the conversion action in Google Ads first (type: Import, from clicks). AxiTrace never creates one for you. |
| Microsoft Advertising | Action (defaults to the key), optional Category and Label, each up to 100 characters. | Create an Event goal in Microsoft Advertising whose Action equals the Action you entered. AxiTrace never creates the goal. |
| LinkedIn Conversions API |
Conversion events only. Conversion rule URN in the form
urn:lla:llaPartnerConversion:12345.
|
Create the conversion rule in Campaign Manager with the Conversions API method, then copy its URN. AxiTrace never creates a rule. |
| Pinterest Conversions API |
Send as: Custom event name (1 to 100 letters, digits, hyphens and underscores)
or Standard event: lead, search, view_category, signup.
|
At most 15 custom events per advertiser, and a custom event stays untracked until you map it in Ads Manager. |
| Reddit Conversions API |
Send as: Custom event name (1 to 64 characters)
or Standard event: Lead, SignUp, ViewContent, Search.
|
Events Manager shows only the 20 most recently used custom names. |
| OpenAI Ads | OpenAI event name: lowercase letters, digits, hyphens and underscores, 2 to 64 characters, starting and ending with a letter or digit; defaults to the key. | Nothing beyond the connection. |
Checking That It Works
- Selector tester: every trigger shows a match count above 0 on the pages where it should fire, and interacting with the element adds a line to the "Would fire" log. See The selector tester.
-
Browser console (code calls): no
[AxiTrace]warning about an unregistered event or a dropped property. In the Network tab, the request to/trackcarries"event": "custom"and your key inevent_name. - Visitor profile: open the Visitors tab and then the visitor. The timeline shows the event with an Engagement or Conversion badge and the display name. Events usually appear within a few seconds; a conversion can take a little longer.
- The Custom Events card: the 7-day count of the definition grows.
- Event feed → Errors: rejected events are listed with the reason. See Error messages.
- Ad platform: the connection's integration log shows each forwarding attempt; the platform's own test or events screen shows the event after its usual delay.
Editing, Disabling and Deleting
| Action | Where | Effect |
|---|---|---|
| Edit | The full list page, Edit | Everything except the event key can be changed. Events already stored keep the values they were received with. |
| Disable | The card or the list page | New events with this key are rejected. The definition, its history and its settings are kept, and it no longer counts toward your plan limit. |
| Enable | The card or the list page | Accepts the key again. Only possible while you are under your plan limit. |
| Delete | The card or the list page, after a confirmation | Works like Disable, and the definition disappears from the card. It stays visible on the full list page, its past events stay on visitor profiles with a deleted badge, and its key cannot be reused. |
How Fast Changes Take Effect
- Server side (HTTP API, PHP SDK and the ingestion of browser events): a NEW definition is accepted within seconds. A disabled or deleted definition stops being accepted within 10 seconds.
- Website SDK: trigger, throttle and pixel bridge changes reach NEW visitor sessions within 5 minutes. A visitor whose session already loaded the configuration keeps it until their next session.
If saving shows the warning "Saved. The ingestion registry could not be updated", your change is stored and the registry repairs itself automatically within 20 minutes. You do not need to save again.
If the ingestion's copy of your registry is temporarily unavailable, events with a valid key format
are ACCEPTED and checked afterwards against your saved definitions: defined keys are processed normally,
undefined keys land in your Errors tab with the unknown custom event: prefix. The website SDK
behaves the same way while its configuration is missing: calls are sent without throttling and the console
shows one custom event config unavailable warning per session.
Consent and Privacy
- Custom events follow the same consent rules as every other event of the website SDK (see Cookie consent). While the SDK waits for consent, calls and trigger fires are held in a short in-memory buffer and sent with their original time once consent is granted. If consent is never granted, nothing is sent.
- When the SDK is disabled for a visitor, for example by Do Not Track where your workspace respects it, custom events are not sent.
- Triggers never read element text or form field values.
- Only declared properties leave AxiTrace, and only to destinations you switched on.
- For server calls you are responsible for sending events only for visitors who consented, like for any other server event.
Limits at a Glance
| Limit | Value |
|---|---|
| Active definitions per workspace | Pro 5, Business 10, Custom unlimited |
| Event key | 1 to 40 characters, [a-z][a-z0-9_] |
| Display name | 80 characters |
| Declared properties per definition | 10 |
| Properties sent per event | 30 |
| Property name | 1 to 40 characters of letters, digits and underscores |
| Property string value | 500 characters |
| Triggers per definition | 5 |
| CSS selector | 300 characters |
| Hover delay | 100 to 5000 ms |
| Visible dwell time | 100 to 60000 ms |
| Every N seconds | 1 to 3600 |
| Unlimited throttle in the browser | 50 events per key per page view |
| Transaction id | 100 characters |
| Rate limit | 600 custom events per minute per workspace |
| Deduplication window (HTTP API) | 5 minutes per event_id |
Error Messages
These messages appear in HTTP API responses and in Event feed → Errors.
| Message | Cause | Fix |
|---|---|---|
unknown custom event: <key> |
The key is not defined, or it is disabled or deleted. | Define or enable it. Check the spelling: keys are case sensitive and lowercase. |
unsupported event: <name> |
The name does not follow the key format, for example it contains uppercase letters, a hyphen or a dot. | Use a key that matches the rules. |
event_name and event_id are required |
HTTP API request without one of them. | Send both. |
custom event <key>: properties: expected at most 30 keys |
More than 30 properties. | Send fewer properties. |
custom event <key>: properties.<name>: expected 1 to 40 characters of [a-zA-Z0-9_] |
Invalid property name. | Rename the property. |
custom event <key>: properties.<name>: expected a scalar value |
An object or array was sent as a property value. | Send a string, number or boolean. |
custom event <key>: properties.<name>: expected at most 500 characters |
String value too long. | Shorten it. |
custom event <key>: value: an engagement event cannot carry a value |
A value was sent for an engagement definition. | Drop the value, or make the definition a conversion. |
custom event <key>: value: expected at least 0 |
Negative value. | Send zero or more. |
custom event <key>: currency: expected an ISO 4217 code |
Currency is not three uppercase letters. | Send for example EUR, not eur or €. |
custom event <key>: transaction_id: expected at most 100 characters |
Transaction id too long. | Shorten it. |
custom event budget exceeded (600/min) |
More than 600 custom events in one minute for the workspace. | Check for a loop or a trigger without a throttle. |
Known Limitations
No-code triggers and Axitrace._instance.track() are NOT available inside the Shopify Web Pixel.
It runs in a sandbox without access to your page, so a trigger engine cannot run there. The HTTP API and
the PHP SDK work, and Shopify order events keep flowing as before.
- The visitor profile timeline shows the most recent 100 events across ALL event types; custom events share that window.
- There are no per-event trend charts or dashboard widgets for custom events yet. Use the 7-day count on the card and the visitor profiles.
- A trigger configured in the panel fills at most one property. Send more from your code.
Troubleshooting
The event does not appear on the profile
- Look in Event feed → Errors for
unknown custom eventor a validation message. - Check that the definition is Active on the card.
- For a trigger or a throttle change, start a NEW browser session: existing sessions keep the configuration they loaded, for up to the session length.
- Check the throttle: with Once per page view a second click on the same page is not sent.
- Check consent: while the visitor has not consented, nothing is sent.
- Make sure you are looking at the right profile: in a private window you are a new visitor.
The tester shows 0 matches
- The element may not exist on this page, or it is rendered later (open the menu or scroll first, then hover the row again).
- Click the element with the tester open to get a proposed selector and compare it with yours.
The tester shows matches but nothing arrives
- The tester tab never sends anything, by design. Test in a normal tab without
?axitrace_test=1, in a new session.
The console says the event is not registered
- The key in your code differs from the defined key, or the definition was created after this session started. Open a new session.
The event is on the profile but not on the ad platform
- Check that the destination is switched on in the definition, not only connected.
- Check the connection's integration log for the platform's answer.
- For Microsoft Advertising, LinkedIn and Google Ads, the goal, rule or conversion action must exist on the platform side.
- For GA4, register the custom dimensions; parameters are invisible in reports until you do.
- An engagement event without any identity signal is not forwarded, see Forwarding.
An HTTP API retry is answered "duplicate"
- That is the deduplication working: an event with this
event_idwas accepted in the last 5 minutes. Use a new id only for a genuinely new occurrence.