The event envelope, money and identifier rules, the standard taxonomy, reserved names, custom schemas and validation.
Every Kentro Pixel event, from the browser or from your server, uses one JSON envelope and one event name from a shared taxonomy. Each name has a JSON Schema, a consent purpose and a list of sources that make it authoritative; this page covers all three and how to check events before you send them.
In this page
- The envelope
- Context
- Money
- Email and phone identifiers
- How to read the taxonomy
- Standard taxonomy
- Which event should I send?
- Reserved for Kentro
- Custom events and the schema registry
- Validating events without sending them
The envelope
The SDK fills in ids, context and consent for you; server integrations build the envelope themselves.
{
"event_id": "evt_192f0c51a2b9f3c7d1e5a2b4c6d8e0f",
"event_name": "commerce.added_to_cart",
"schema_version": 1,
"occurred_at": "2026-10-09T17:00:00.000Z",
"anonymous_id": "anon_6f1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e",
"session_id": "sess_0a1b2c3d4e5f60718293a4b5",
"subject_ref": null,
"commerce_subject_ref": null,
"order_ref": null,
"campaign_ref": "cmp_spring_launch",
"creative_ref": null,
"landing_page_ref": null,
"experiment_ref": null,
"experiment_variant_ref": null,
"context": {
"page_url": "https://www.example.com/products/mug?utm_source=google&utm_medium=cpc&gclid=abc123",
"page_title": "Mug",
"referrer_domain": "www.google.com",
"device_class": "desktop",
"locale": "en-US",
"sdk": { "name": "kentro-pixel-js", "version": "1.3.0" }
},
"consent": { "analytics": "granted", "marketing": "denied", "functional": "granted", "policy_version": "2026-10", "source": "cmp", "recorded_at": "2026-10-09T16:59:00Z", "region": "US-CA", "gpc": false },
"properties": { "product_id": "sku-123", "quantity": 1, "unit_price": { "amount_minor": 1999, "currency": "USD" } }
}
| Field | Type and limits | Notes |
|---|---|---|
event_id | string, 1 to 128 characters | Unique per real-world event and stable across retries; deduplicated per property. When omitted, Kentro derives it from a hash of the envelope, so two identical envelopes count once. |
event_name | string, 3 to 128 characters | namespace.name in lower case: letters, then a dot, then letters, digits or underscores. |
schema_version | integer, 1 to 1000 | Defaults to 1. |
occurred_at | ISO 8601 timestamp | Defaults to the time of receipt. Rejected with CLOCK_SKEW when more than 7 days in the past or 5 minutes in the future. |
anonymous_id, session_id | string up to 128 characters, or null | Browser identifiers; the SDK fills them. |
subject_ref, commerce_subject_ref | string up to 256 characters, or null | Pseudonymous person references: your login id, your commerce customer id. Kept as sent on server events; on browser events set only through identity_token. |
identity_token | string up to 1,024 characters | Browser events: an identity token your server signed (see Identity tokens). Verified on arrival, never stored. |
order_ref, campaign_ref, creative_ref, landing_page_ref, experiment_ref, experiment_variant_ref | string up to 256 characters, or null | Join keys for orders, Kentro GO campaigns, creatives, landing pages and experiments. |
context | object | Page and device context, see below. |
consent | object | Consent snapshot. A missing purpose is unknown. |
properties | object | Event-specific data, validated against the event's schema. |
An envelope may be at most 32 KB serialized. Unknown top-level fields are dropped. Kentro adds the workspace, property, source type, environment and receipt time itself.
Context
context accepts:
| Field | Notes |
|---|---|
page_url, page_title | Keeps only utm_* and click ids from the query string, loses its fragment and credentials, and has sensitive paths redacted. |
referrer or referrer_domain | Reduced to a host name. |
utm | source, medium, campaign, content, term, id. |
click_ids | gclid, gbraid, wbraid, fbclid, ttclid, msclkid. Raw values stay only with marketing consent; otherwise they are kept as SHA-256 hashes. |
device_class, bot | Derived from the request's user agent for browser sources; settable by server sources. |
locale, timezone, app_version, source_channel, sdk | Descriptive context. |
ga4_client_id | Your site's GA4 client id (the _ga cookie value), used for GA4 deliveries. |
client_user_agent | Server sources: the visitor's browser user agent, for Meta Conversions API matching. Kept only on accepted events whose consent grants marketing. |
Property values whose key ends in url, uri, href, link or path get the same URL treatment as page_url.
Money
Money is always an object with integer minor units and an upper-case ISO 4217 code:
{ "amount_minor": 4999, "currency": "USD" }
4999 is 49.99 USD; for zero-decimal currencies such as JPY, amount_minor is the whole amount. Floats, strings and extra keys are rejected with SCHEMA_VIOLATION.
Email and phone identifiers
- Raw
emailandphonevalues at the top level ofpropertiesor underproperties.customerare replaced on ingestion byemail_sha256andphone_sha256(email trimmed and lower-cased, phone reduced to its digits, then SHA-256 as lower-case hex); the raw value is discarded. - The builtin schemas accept identifiers only inside
customer, so sendproperties.customer.email(or a pre-hashedemail_sha256), never a top-levelemail. If you hash yourself, use the same normalization. - The shared
customerobject acceptsref,email_sha256,phone_sha256,country(2 characters) andmarketing_opt_in(boolean). - In custom events, a top-level property whose name ends like a contact field (
customer_email,email_address,phone_number,mobile) is replaced by<name>_sha256. In every event, email addresses inside other strings become[email]and phone numbers inside free text become[phone].
How to read the taxonomy
Three separate things describe every event:
- What happened: the namespace and the Category column.
marketing.*events are website and acquisition behavior;commerce.*events are shopping and business records. The categoryopsmarks Kentro GO operations, which Kentro GO records itself. - Which consent it needs: the Consent purpose column. The purpose is fixed per event and does not follow the namespace. Most
marketing.*website events need analytics consent; lead, opt-in and acquisition events need marketing consent; browser shopping events need analytics consent; order, payment, refund, return, subscription, customer and fulfillment records are functional. - Whether it is a fact: the Authoritative from column. Orders, refunds and other revenue facts are authoritative only from a server source; a browser copy is accepted as a signal.
| Purpose | What the consent covers | Events |
|---|---|---|
analytics | Measuring how people use your site and store. | Page views, sessions, site search, content views, CTA clicks, form starts and submissions, signups, experiment exposures and asset engagement; browser shopping events. |
marketing | Leads, opt-ins and acquisition. | marketing.lead_captured, marketing.email_opt_in_recorded, marketing.ad_clicked, marketing.lead_qualified, marketing.demo_booked. |
functional | Business records you must keep. Collected unless the snapshot explicitly says functional: "denied". | Orders and payments, cancellations, refunds, returns, subscriptions, customer records, fulfillment and shipping costs. |
The purpose decides whether Kentro collects an event, not whether it is exported. Ad networks receive a person only with an explicit marketing grant, even for a functional order (see Destinations).
Standard taxonomy
Every builtin event has an allowlist of properties (additionalProperties is false), so unlisted keys are rejected. Strings are at most 256 characters unless the full schema says otherwise; counts are non-negative integers (quantity and step start at 1); items is an array of up to 200 line items (product_id and quantity required; variant_id, sku, name, category, unit_price and total optional). Browser or server covers browser, server, Shopify pixel, Kentro OS and Kentro GO landing pages; Server only covers the server API, Kentro OS, CRM and imports. Events from any other source are accepted but flagged is_authoritative: false. The full JSON Schemas are available from GET /v1/event-schemas.
| Event | Category | Required properties | Other allowed properties | Consent purpose | Authoritative from |
|---|---|---|---|---|---|
marketing.page_viewed | marketing | none | page_type, page_name, page_path, title | analytics | Browser or server |
marketing.session_started | marketing | none | entry_url, referrer_domain, landing | analytics | Browser or server |
marketing.product_viewed (deprecated: recorded as commerce.product_viewed) | marketing | product_id | product_name, category, price (money) | analytics | Browser or server |
marketing.search_performed | marketing | query | results_count, category | analytics | Browser or server |
marketing.content_viewed | marketing | none | content_id, content_type, content_name | analytics | Browser or server |
marketing.cta_clicked | marketing | cta_id | cta_text, destination_url, location | analytics | Browser or server |
marketing.form_started | marketing | form_id | form_name | analytics | Browser or server |
marketing.form_submitted | marketing | form_id | form_name, fields (field names only), success, customer | analytics | Browser or server |
marketing.lead_captured | marketing | none | lead_ref, form_id, source, customer | marketing | Browser or server |
marketing.signup_completed | marketing | none | method, plan, customer | analytics | Browser or server |
marketing.email_opt_in_recorded | marketing | none | list_id, source, double_opt_in, customer | marketing | Browser or server |
marketing.experiment_exposed | marketing | none | arm | analytics | Browser or server |
marketing.asset_engaged | marketing | asset_ref | asset_type, action, seconds | analytics | Browser or server |
commerce.product_viewed | commerce | product_id | variant_id, sku, name, category, brand, price (money), in_stock | analytics | Browser or server |
commerce.collection_viewed | commerce | collection_id | name, product_count | analytics | Browser or server |
commerce.added_to_cart | commerce | product_id, quantity | variant_id, sku, name, unit_price (money), cart_id | analytics | Browser or server |
commerce.removed_from_cart | commerce | product_id, quantity | variant_id, sku, name, cart_id | analytics | Browser or server |
commerce.checkout_started | commerce | none | cart_id, checkout_id, total (money), item_count | analytics | Browser or server |
commerce.checkout_step_completed | commerce | step | checkout_id, step_name | analytics | Browser or server |
commerce.payment_attempted | commerce | none | checkout_id, payment_method, amount (money), success | analytics | Browser or server |
commerce.order_placed | commerce | order_id, total (money) | subtotal, tax, shipping, discount (money), items, item_count, coupon_code, payment_method, customer | functional | Server only |
commerce.order_paid | commerce | order_id, total (money) | payment_method, transaction_ref | functional | Server only |
commerce.order_cancelled | commerce | order_id | reason | functional | Server only |
commerce.order_refunded | commerce | order_id, amount (money) | reason, refund_ref | functional | Server only |
commerce.order_partially_refunded | commerce | order_id, amount (money) | items, reason, refund_ref | functional | Server only |
commerce.return_created | commerce | order_id | return_ref, items, reason | functional | Server only |
commerce.subscription_started | commerce | subscription_id | plan, amount (money), interval | functional | Server only |
commerce.subscription_renewed | commerce | subscription_id | amount (money), period_end | functional | Server only |
commerce.subscription_cancelled | commerce | subscription_id | reason | functional | Server only |
commerce.customer_created | commerce | customer | none | functional | Server only |
commerce.customer_updated | commerce | customer | none | functional | Server only |
commerce.fulfillment_shipped | commerce | order_id | fulfillment_ref, carrier, tracking_ref | functional | Server only |
commerce.fulfillment_delivered | commerce | order_id | fulfillment_ref, delivered_at | functional | Server only |
commerce.shipping_cost_finalized | commerce | order_id, shipping_cost (money) | carrier | functional | Server only |
marketing.ad_clicked (optional) | marketing | provider | ad_ref, click_id_type | marketing | Browser or server |
marketing.lead_qualified (send from your server) | marketing | lead_ref | score, stage | marketing | Server only |
marketing.demo_booked | marketing | none | lead_ref, scheduled_at, source | marketing | Browser or server |
marketing.product_viewedis deprecated. Sendcommerce.product_viewed. The old name is still accepted, validated against its own schema, then recorded ascommerce.product_viewedwithproduct_namerenamed toname. The per-event result carries"recorded_as": "commerce.product_viewed", and the event debugger shows"renamed_from": "marketing.product_viewed". Audience rules and destination mappings must usecommerce.product_viewed.marketing.lead_qualified: send it from your server when a lead is qualified in your CRM or sales process. A browser copy is accepted but not authoritative.marketing.ad_clickedis optional: send it for an ad click you observed yourself, for example through a redirect service. Kentro already records ad clicks from the click ids on page views.
Which event should I send?
| To record | Send | Consent purpose | Conversion |
|---|---|---|---|
| A page view | Nothing: the SDK sends marketing.page_viewed on every page load and route change, and marketing.session_started when a session begins. Call page only for extra page views. | analytics | No |
| A product view | commerce.product_viewed | analytics | No |
| An add to cart | commerce.added_to_cart, and commerce.removed_from_cart for removals | analytics | No |
| A checkout | commerce.checkout_started, then commerce.checkout_step_completed for each step | analytics | No |
| A purchase | From your server: commerce.order_paid (and commerce.order_placed if you record orders before payment). From the browser: commerce.order_placed, accepted as a non-authoritative signal. | functional | Yes, once per order |
| A refund or cancellation | From your server: commerce.order_refunded, commerce.order_partially_refunded or commerce.order_cancelled | functional | Corrects the order's conversion |
| A lead form | marketing.lead_captured; add marketing.form_submitted to measure any form, lead or not | marketing (lead_captured), analytics (form_submitted) | marketing.lead_captured only |
| A newsletter or list opt-in | marketing.email_opt_in_recorded | marketing | No |
| A signup | marketing.signup_completed | analytics | Yes |
| A booked demo | marketing.demo_booked | marketing | Yes |
| A lead qualified in your CRM | From your server: marketing.lead_qualified | marketing | No |
| A subscription | From your server: commerce.subscription_started, commerce.subscription_renewed, commerce.subscription_cancelled | functional | No |
- One order counts once. Analytics keep one conversion per order reference: a server copy wins over a browser copy and
commerce.order_paidovercommerce.order_placed. Refunds and cancellations correct that conversion and never add a new one. - One form submission counts once. Kentro GO landing pages send
marketing.form_submitted(field names only) andmarketing.lead_captured(hashed email and phone) for each submission; onlymarketing.lead_capturedis a conversion. Lead events of the same type with the samelead_refalso count once. - Conversion types. Conversion facts have one of seven types:
lead_captured,signup_completed,demo_booked,order_placedandorder_paidare conversions;order_refunded(full and partial refunds) andorder_cancelledcorrect an order. These seven values are the only onesGET /v1/analytics/conversions?type=and audience conversion conditions accept. In Kentro GO, theAnalyticspage'sConversionsview shows the totals by type.
Reserved for Kentro
Eighteen names describe facts Kentro computes itself, operations Kentro GO performs, or the history Kentro Email records. You cannot send them: every route (browser, batch, GIF beacon, server API and the validate endpoint) rejects them with RESERVED_EVENT, whatever the schema_version, with a reason that says what to use instead. GET /v1/event-schemas still lists them with reserved: true and that explanation in reserved_reason. Pixel audience rules and destination mappings that name them are refused.
| Event | Why it is reserved | Use instead |
|---|---|---|
marketing.purchase_attributed | Computed by Kentro attribution. | GET /v1/analytics/attribution |
marketing.conversion_confirmed | Derived by Kentro from authoritative order and lead events. | Send commerce.order_placed, commerce.order_paid or marketing.lead_captured from your server, and read GET /v1/analytics/conversions. |
marketing.audience_joined, marketing.audience_exited | Kentro records audience membership at each audience recompute. | The audience endpoints (/v1/audiences) and webhook audience syncs. |
marketing.campaign_impression | Synced by Kentro GO from the ad providers' insights. | Campaign results in Kentro GO. |
marketing.campaign_activated, marketing.campaign_paused | Recorded by Kentro GO when it activates or pauses a campaign (category ops). | Nothing: they are never sent by a website or a server. |
email.subscribed, email.unsubscribed | The Kentro Email consent history. | Change a contact's subscription in Kentro Email. |
email.sent, email.delivered, email.delivery_delayed, email.bounced, email.complained, email.rejected, email.rendering_failed, email.opened, email.clicked | The Kentro Email sending history. | Read campaign and flow results in Kentro Email. |
Kentro Email segments can use the email.* events; Pixel audiences cannot. A Kentro Email link click also records an email touchpoint for attribution (see Processing).
Custom events and the schema registry
Custom event schemas are not available in this preview: you cannot create them, and the collector accepts the builtin taxonomy above. When they become available, these rules apply:
- A custom schema (an event name, a version, a category and a JSON Schema for
properties) is validated exactly like a builtin. A builtin with the same name always wins, and a reserved name stays refused. - Its consent purpose is
functionalwhen its category isopsandanalyticsotherwise; a custom event never has the marketing purpose. - It is authoritative only when sent through the server API.
- Registry changes reach the collector within 60 seconds.
The schema listing:
| Request | Answer |
|---|---|
GET https://apis.kentro.live/pixel/v1/event-schemas (Kentro authentication) | { "builtin": [...], "custom": [...] }: the builtin taxonomy and your workspace's custom schemas. |
GET https://pixel.kentro.live/v1/event-schemas (public, cached for 5 minutes) | The builtins only, with "custom": []. |
Each builtin entry carries event_name, version, category, required, schema, authoritative_sources, consent_purpose, description, reserved, reserved_reason, replaced_by and builtin: true. replaced_by names the event a deprecated name is recorded as ("commerce.product_viewed" for marketing.product_viewed, null otherwise). Each custom entry carries event_name, version, category, schema, status, builtin: false, reserved: false, reserved_reason: null and replaced_by: null.
Validating events without sending them
POST /v1/event-schemas/validate runs the ingestion checks (envelope, clock skew, schema, identity tokens, sensitive-data detectors and the consent decision) on one envelope or { "events": [...] } (1 to 100), evaluated as browser events, and stores nothing.
- Authenticate with a property key (
X-Kentro-Pixel-Keyheader,keyquery parameter orkeyin the body) on the collector host, or with your Kentro credentials on the management host. With a property key the property's settings apply; with Kentro credentials the default settings do. - It does not check the origin allowlist, the per-key event rate limit or duplicates; requests count against the collector's per-IP limits.
- In Kentro GO, the property's
Validatetab runs the same check.
curl -sS https://pixel.kentro.live/v1/event-schemas/validate \
-H "X-Kentro-Pixel-Key: pk_live_Xq3M9b2LkV7tR1sNw8YhC4Ze" \
-H "Content-Type: application/json" \
-d '{
"events": [
{ "event_id": "evt_1", "event_name": "commerce.added_to_cart", "consent": { "analytics": "granted" }, "properties": { "product_id": "sku-1", "quantity": 1, "unit_price": { "amount_minor": 1999, "currency": "USD" } } },
{ "event_id": "evt_2", "event_name": "commerce.added_to_cart", "consent": { "analytics": "granted" }, "properties": { "product_id": "sku-1", "quantity": 1, "unit_price": { "amount_minor": 19.99, "currency": "USD" } } },
{ "event_id": "evt_3", "event_name": "marketing.product_viewed", "consent": { "analytics": "granted" }, "properties": { "product_id": "sku-1", "product_name": "Mug" } },
{ "event_id": "evt_4", "event_name": "marketing.purchase_attributed", "properties": { "order_ref": "order-1001", "model": "last_touch" } }
]
}'
{
"results": [
{ "index": 0, "event_id": "evt_1", "event_name": "commerce.added_to_cart", "valid": true, "outcome": "accepted", "reason_code": null },
{ "index": 1, "event_id": "evt_2", "event_name": "commerce.added_to_cart", "valid": false, "reason_code": "SCHEMA_VIOLATION", "reason": "properties/unit_price/amount_minor must be integer" },
{ "index": 2, "event_id": "evt_3", "event_name": "marketing.product_viewed", "valid": true, "outcome": "accepted", "reason_code": null, "recorded_as": "commerce.product_viewed" },
{ "index": 3, "event_id": "evt_4", "event_name": "marketing.purchase_attributed", "valid": false, "reason_code": "RESERVED_EVENT", "reason": "marketing.purchase_attributed is computed by Kentro attribution; read it from the attribution endpoints (GET /v1/analytics/attribution) instead of sending it" }
]
}
outcome is accepted, quarantined or suppressed, exactly what ingestion would record. recorded_as is present when the event was sent under a deprecated name.

