SDK
The window.zappush API. Send standard and custom events with the full payload, manage consent, and read shopper identity.
Once a Zappush script is installed it exposes window.zappush. Your own code
calls it to send events, manage consent, and read the current shopper identity.
Use the SDK when you do not have a data layer, when the event has no GA4
equivalent, or when you want contact details to stay out of
window.dataLayer, where every other script on the page can read them.
What is available where
Zappush serves two scripts and they expose different methods. The tracker runs on your store, the gate runs on a compliant page.
| Method | Tracker | Gate |
|---|---|---|
track | Yes | Yes |
setConsent, withdrawConsent | Yes | No |
getIdentity | No | Yes |
redirect | No | Yes |
track is safe to call at any time. The install snippet buffers calls made
before the script loads and replays them once it is ready. Every other method
needs the script loaded first. If you have not installed it yet, follow
Install the tracker.
Payload shape
window.zappush.track(eventName, metadata);Everything goes in one flat object. There is no ecommerce or user_data
wrapper, unlike a data layer push.
window.zappush.track("checkout_started", {
currency: "USD", // conversion value
value: 158.0,
email: "alex@example.com", // who the shopper is
phone: "+61400000000",
items: [ ... ], // which products
});| Group | Keys |
|---|---|
| Value | value, currency, transaction_id, coupon |
| Contact | email, phone, first_name, and the rest below |
| Products | items |
| Anything else | Passed through as event metadata |
value must be a number greater than zero, and currency an ISO 4217 code.
The conversion value Meta receives is the value you send. It is never summed
from item prices, so an event without one reports a conversion worth nothing.
Event names
This is the one place the SDK differs from the data layer. A data layer push
accepts the GA4 name and translates it. track does not translate anything, so
pass the Zappush name.
window.zappush.track("checkout_completed", { ... }); // correct
window.zappush.track("purchase", { ... }); // a custom event named "purchase"Calling track("purchase") is not an error and the event will arrive, but it
does not become an order, and it reaches Meta as a custom event rather than the
standard Purchase.
| Zappush event | Meta CAPI event | Fire when |
|---|---|---|
product_viewed | ViewContent | A product page or prominent product modal opens |
product_added_to_cart | AddToCart | A product is added to the cart |
cart_viewed | custom | The cart page or drawer opens |
checkout_started | InitiateCheckout | The shopper reaches the first checkout step |
checkout_shipping_info_submitted | AddShippingInfo | Shipping details are submitted |
payment_info_submitted | AddPaymentInfo | Payment details are submitted |
checkout_completed | Purchase | Payment succeeded |
order_refunded | custom | An order is refunded |
lead_generated | SubmitApplication | Contact details are captured |
schedule | Schedule | A call or appointment is booked |
verify_lead | Lead | A captured lead is confirmed or qualified |
search_performed | Search | A search is run |
user_signed_up | CompleteRegistration | An account is created |
The full list, including the GA4 name each one maps from, is in the event name reference.
Contact fields
These decide whether a conversion matches a real person in Meta. Email and phone do most of the work. Send them flat, on every event that has them.
| Field | Also accepted as |
|---|---|
email | |
phone | phone_number |
first_name | firstName |
last_name | lastName |
city | |
state | region |
zip | postal_code, zipcode |
country | country_code |
date_of_birth | dob |
gender | |
external_id | external_customer_id |
All optional, send whatever you have. Phone in E.164 with the country code, country as ISO 3166-1 alpha-2. Zappush persists them for the session, so later events in that session attribute to the same person without resending.
A nested user_data object is also accepted, for when you are porting a data
layer payload, but flat keys are the recommended shape.
Items
| Field | Required | What it does |
|---|---|---|
item_id | Yes | Identifies the product. Sent to Meta as content_ids. |
item_name | No | Product name in your dashboard. Used as the identity if item_id is missing. |
quantity | No | Summed into Meta's num_items. Defaults to 1. |
price | No | Unit price in your dashboard and on order lines. |
item_variant | No | Variant name in your dashboard. |
item_category | No | Product type in your dashboard. |
Ecommerce events
Product viewed
window.zappush.track("product_viewed", {
currency: "USD",
value: 79.0,
items: [
{
item_id: "SKU-001",
item_name: "Running Shoes",
item_variant: "Blue / 10",
item_category: "Footwear",
price: 79.0,
quantity: 1,
},
],
});Added to cart
window.zappush.track("product_added_to_cart", {
currency: "USD",
value: 79.0,
items: [
{ item_id: "SKU-001", item_name: "Running Shoes", price: 79.0, quantity: 1 },
],
});Checkout started
window.zappush.track("checkout_started", {
currency: "USD",
value: 158.0,
coupon: "SUMMER10",
items: [
{ item_id: "SKU-001", item_name: "Running Shoes", price: 79.0, quantity: 2 },
],
});Payment info submitted
window.zappush.track("payment_info_submitted", {
currency: "USD",
value: 158.0,
email: "alex@example.com",
phone: "+61400000000",
items: [
{ item_id: "SKU-001", item_name: "Running Shoes", price: 79.0, quantity: 2 },
],
});Purchase
Fire on the order confirmation page, after payment succeeds.
window.zappush.track("checkout_completed", {
transaction_id: "T-1234",
currency: "USD",
value: 158.0,
coupon: "SUMMER10",
email: "alex@example.com",
phone: "+61400000000",
first_name: "Alex",
last_name: "Smith",
city: "Sydney",
state: "NSW",
zip: "2000",
country: "AU",
items: [
{
item_id: "SKU-001",
item_name: "Running Shoes",
item_variant: "Blue / 10",
item_category: "Footwear",
price: 79.0,
quantity: 2,
},
],
});valuemust be a number greater than zero. Without it the purchase does not become an order in your dashboard and reports no revenue.transaction_ididentifies the order. It is what stops a refreshed confirmation page from counting twice, and it is sent to Meta asorder_id.- Contact details matter most here. Without them Zappush cannot match the order to a shopper, and the conversion will not attribute in Meta.
Lead events
Lead captured
window.zappush.track("lead_generated", {
lead_source: "Quiz",
email: "alex@example.com",
phone: "+61400000000",
first_name: "Alex",
last_name: "Smith",
});Appointment booked
Fire schedule when someone books a call or an appointment. It is a stronger
signal than a form submit.
window.zappush.track("schedule", {
email: "alex@example.com",
phone: "+61400000000",
first_name: "Alex",
last_name: "Smith",
});Lead verified
Fire verify_lead when a lead you already captured is confirmed or qualified,
for example an email or phone confirmation. The initial capture is
lead_generated.
window.zappush.track("verify_lead", {
email: "alex@example.com",
phone: "+61400000000",
});Send contact fields only on lead events. No value, currency, or product data unless the lead has a real monetary worth.
Custom events
Any name that is not a standard event passes through as a custom event. Nothing to register first.
window.zappush.track("quiz_completed", {
quiz_id: "skin-type",
result: "combination",
});Custom events appear in your dashboard once the first one arrives. They start unclassified, so give them a funnel stage on the analytics configure page to make them count in your funnel.
They reach Meta as a custom event under the same name, carrying every key you
sent except items, which is dropped, and contact fields, which Zappush forwards
separately as user data. So quiz_id and result above arrive as custom
properties you can build an audience on.
Attach value and currency if the action is worth something, and the custom
event can be optimized against like any other conversion.
Keep names lowercase with underscores, like quiz_completed, and stay
consistent so the same action always reports under one name. A name is created
the first time it arrives, so a typo becomes a second event you then have to
ignore.
From a third-party tool
If you send events from a script that loads independently of Zappush, a quiz
platform or a booking widget for example, call track from its custom
JavaScript field. Buffered calls are replayed, so there is no readiness check to
write.
window.zappush.track("quiz_completed", { result: "type-a" });Manage consent
Tracker only. Call these from your own consent banner. Both take effect immediately.
window.zappush.setConsent({ analytics: true, marketing: true });
window.zappush.withdrawConsent();setConsent takes both booleans. Analytics consent lets Zappush attribute the
visitor's events, marketing consent lets it forward marketing identifiers.
Granting consent also starts data layer interception, so pushes are only picked
up once consent is resolved.
withdrawConsent clears the visitor's stored data and stops tracking until
consent is granted again.
Only call setConsent once the shopper has actually agreed. Calling it without
real consent defeats the purpose of the banner.
Read the current identity
Gate only. Returns what Zappush has resolved for the visitor on a compliant page.
const identity = window.zappush.getIdentity();| Field | What it is |
|---|---|
persistent_id | Long-lived ID for this shopper |
session_id | ID for the current session |
route_id | The route this compliant page belongs to |
fbc, fbp, fbclid | Meta click and browser identifiers |
consent | Current analytics and marketing grants |
Contact fields, page URL, referrer, and user agent are also on the object. Any
field Zappush has not resolved yet is null.
Trigger a compliant redirect
Gate only. Sends the visitor to your store and carries their identity across. The gate script already wires this to the page's call to action, so most stores never call it. Use it if you build your own button.
window.zappush.redirect("https://your-store.example/products/best-seller");Repeat calls within half a second are ignored, so a double click cannot fire two redirects. If building the identity-carrying URL fails for any reason, the visitor is still sent to the plain destination URL rather than being stranded.