An event goal counts the visits in which your site sends a named event. Your developer adds one line of code where the action happens, for example after a form was sent successfully. In the dashboard, this goal type is called An event from your code.
When to use event goals
Best for tracking:
- Forms that show a message instead of opening a thank-you page
- Purchases with a real amount
- Video interactions (play, finished)
- Add to cart actions
- Feature use in web apps and single-page apps
- Any action that has no page of its own
How it works
- Your developer adds one line of code to your site
- The line runs when the action happens and sends the event to Zenovay
- Your event goal counts every visit in which the event arrived (at most once per visit)
Sending an event
// Simple event
zenovay('track', 'signup');
// Event with its own value
zenovay('track', 'purchase', { value: 99.99 });
The first argument is always the word 'track', the second is your event name, and the third is optional extra information.
Creating the goal
Open the Goals sub-tab
Open your website from Domains, select the Journeys tab, then the Goals sub-tab, and click New goal.
Choose An event from your code
In the first step, choose An event from your code. The template Purchases also creates an event goal.
Pick the event
The assistant lists the events your site already sent in the selected period, with how often each arrived. Pick yours, or type its name. The assistant also shows the line of code to add.
Wait for the live check
If the event has not arrived yet, a live check waits for it and turns green when the first event arrives. It updates by itself.
Name and value
Give the goal a name. Optionally turn on Give each conversion a value, and leave Count past visits too on to count events from the last 12 months.
Save
Click Create goal.
Recording a goal directly from your code
Instead of sending an event and creating the goal in the dashboard, your code can record a goal directly:
zenovay('goal', 'purchase', { value: 99.99, currency: 'USD' });
- The first time Zenovay receives a goal name it doesn't know, it creates the goal automatically.
- These goals are marked From your code in the goal list and count toward your plan's goal limit.
- If such a goal stops arriving, it is flagged Not seen in your code for 7 days.
- A goal call records
valueandcurrency.
Event names
Rules
- Upper and lower case do not matter:
Signup,signupandSIGNUPare the same event. - Spaces count as underscores:
Sign Upis the same assign_up. - Other differences do matter:
signupandsign_upare two different events. - Names may use letters, numbers, spaces and
_.:/-, with at most 64 characters.
Good names
| Good names | Unclear names |
|---|---|
signup_complete | goal1 |
purchase | click |
video_50_percent | event |
add_to_cart | 123 |
Naming conventions
Pick one style and keep it, for example snake_case: form_submit. Remember that formSubmit and form_submit are different names.
Group events with prefixes
// E-commerce events
zenovay('track', 'ecom_add_to_cart');
zenovay('track', 'ecom_checkout_start');
zenovay('track', 'ecom_purchase');
// Engagement events
zenovay('track', 'engage_video_play');
zenovay('track', 'engage_scroll_50');
Common implementations
Form submission
document.querySelector('form').addEventListener('submit', function(e) {
zenovay('track', 'contact_form');
});
// Or for AJAX forms, after the server confirmed
function onFormSuccess() {
zenovay('track', 'contact_form');
}
Video interactions
const video = document.querySelector('video');
video.addEventListener('play', function() {
zenovay('track', 'video_play');
});
video.addEventListener('ended', function() {
zenovay('track', 'video_complete');
});
// 50% watched
video.addEventListener('timeupdate', function() {
if (video.currentTime / video.duration > 0.5) {
zenovay('track', 'video_50_percent');
}
});
A goal counts each visit only once, so it does not matter if the 50% line runs several times.
E-commerce actions
// Add to cart
function addToCart(product) {
// Your cart logic...
zenovay('track', 'add_to_cart', {
value: product.price
});
}
// Purchase complete
function onPurchaseComplete(order) {
zenovay('track', 'purchase', {
value: order.total
});
}
Sign-up
async function handleSignup(formData) {
try {
const response = await api.signup(formData);
if (response.success) {
zenovay('track', 'signup_complete');
}
} catch (error) {
// Handle error
}
}
Values
Fixed value
Set it in the goal (step 3 of the assistant):
- Turn on Give each conversion a value
- Enter the Value per conversion and the Currency, for example 50 EUR
- Every conversion counts with that value
Value sent with the event
Send the real amount from your code:
// Order value
zenovay('track', 'purchase', {
value: orderTotal
});
// Calculated value
const leadValue = isPremium ? 100 : 25;
zenovay('track', 'lead', { value: leadValue });
Value read from the page
const price = parseFloat(
document.querySelector('.price').textContent.replace('$', '')
);
zenovay('track', 'purchase', { value: price });
Always send value as a plain number, without a currency symbol and not as text.
Framework integrations
React
import { useCallback } from 'react';
function SignupForm() {
const handleSubmit = useCallback((data) => {
// Submit logic...
window.zenovay('track', 'signup');
}, []);
return <form onSubmit={handleSubmit}>...</form>;
}
Vue
<script>
export default {
methods: {
handleSubmit() {
// Submit logic...
window.zenovay('track', 'signup');
}
}
}
</script>
Next.js
// Client-side tracking inside a component
'use client';
export default function CheckoutButton() {
function handleSuccess() {
if (typeof window !== 'undefined') {
window.zenovay('track', 'purchase');
}
}
return <button onClick={handleSuccess}>Complete</button>;
}
Single page apps (SPAs)
Route changes
For virtual page views:
// React Router
history.listen(() => {
zenovay('page');
});
// Vue Router
router.afterEach(() => {
zenovay('page');
});
Event after navigation
// Navigate, then send the event
router.push('/success').then(() => {
zenovay('track', 'signup_complete');
});
Conditional tracking
// Only send the event if conditions are met
if (user.isNewUser && !user.hasConvertedBefore) {
zenovay('track', 'first_conversion');
}
// A/B test variant
if (experimentVariant === 'B') {
zenovay('track', 'variant_b_conversion');
}
Error handling
Safe tracking
The tracking snippet defines a small command queue, so calls made before the script finished loading are still processed once it has. If you want to guard against the script being blocked entirely:
function safeTrack(eventName, properties) {
if (typeof zenovay === 'function') {
zenovay('track', eventName, properties);
} else {
console.warn('Zenovay not loaded');
}
}
Testing events
Live check in the assistant
The easiest test: open the goal assistant, choose your event, then trigger the action on your site. The live check turns green when the event arrives.
Debug mode
Turn on console logging by adding data-debug="true" to the tracking script tag:
<script defer
data-tracking-code="YOUR_TRACKING_CODE"
data-debug="true"
src="https://api.zenovay.com/z.js">
</script>
Then send a test event and check the console for output:
zenovay('track', 'test_event');
Remove the attribute again when you are done testing.
Check in the dashboard
- Send a test event
- Open the Live View tab to watch activity in real time, or open Journeys → Goals and the goal's details
- Check that Latest conversions shows your visit
- Check the value
Network tab
- Open DevTools → Network
- Send the event
- Look for a request to
api.zenovay.com - Check the payload
Troubleshooting
Events are not counted
Check that the script loaded:
console.log(typeof zenovay); // Should be 'function'
Check the event name:
- Goal:
signup - Code:
zenovay('track', 'signup')counts - Code:
zenovay('track', 'Signup')counts too (upper and lower case don't matter) - Code:
zenovay('track', 'sign_up')does not count (a different name)
Check for errors:
- Browser console for errors
- Network tab for failed requests
The value is missing
Make sure the value is a number:
// Wrong
zenovay('track', 'purchase', { value: "$99.99" });
// Correct
zenovay('track', 'purchase', { value: 99.99 });