Payload Reference
The payload has the same shape for every event and every platform.
Keys marked always sent arrive on every event — when they do not apply they come empty ("", 0, [], {}, null) rather than missing, so you can read them without existence checks. The only exception is the order lines, where a few keys may be absent entirely; those are marked in place.
Complete Reference
event object always sent
What happened, when, and what it was worth.
name string
Event name: checkout_completed, product_viewed, lead_created.
name: "checkout_completed"
id string
Id from the source platform — order number, lead id. "" when there is none.
id: "13195238211910"
unique_id string
Unique id of this instance, and the recommended idempotency key. Present on orders and leads, "" elsewhere.
When the platform provides no id, we send a generated one, unique per page load — it does not guarantee deduplication. Use deduplication_id in that case.
unique_id: "13195238211910"
deduplication_id string
Present on every event, so it is the idempotency key for those without a unique_id.
It is derived: on orders and leads it contains unique_id, so the two are not independent — do not treat them as separate keys. Elsewhere it is built from the event time and the page load, so it is stable for the same event resent, and different between two visits.
deduplication_id: "idx13195238211910"
Both identifiers are opaque: compare them, do not parse them. The format may change.
timestamp int
When the event happened, ms Unix UTC.
timestamp: 1786202400000
value number
Total value including tax, in currency. Includes shipping and other components as the shop declares them. 0 on events without a value.
value: 164.99
currency string
ISO 4217, three letters.
currency: "RON"
exchange_rate number
Rate declared by the platform when the order currency differs from the base one. 1 otherwise.
exchange_rate: 1
reason string | null
Why the event happened — a cancellation or disqualification reason. Webhook routes only; null elsewhere.
reason: "out_of_stock"
properties object
The shop's own attributes, exactly as the source sends them. See properties.
properties: { gift_wrap: ["yes"] }
context object always sent
The page, the device and the route the event arrived on.
url string
The page where the event happened.
url: "https://shop.example/checkout"
referrer_url string
The referring page. "" on direct access, or when the referrer is not relevant for attribution.
referrer_url: "https://www.google.com/"
landing_url string
First page of the current visit, with its campaign parameters.
landing_url: "https://shop.example/lp?utm_source=facebook"
timestamp int
Page load time, ms Unix UTC. Shared by every event from the same load, unlike event.timestamp.
timestamp: 1786202390000
last_ad_source string
The most recent paid source. "" when there is none.
last_ad_source: "facebook"
integration string
The route the data arrived on: browser, webhook <platform>, or both joined by | when the same event was seen on both and merged.
integration: "browser|webhook merchantpro"
data_source string
Business origin: website, admin, phone, app, or a label of the shop's own.
data_source: "website"
script_id string
Identifier of the shop configuration, 8 characters.
script_id: "YOURSCR1"
page_type string
Page type: home, product, category, checkout.
page_type: "checkout"
has_adblocker int
1 if an ad blocker was detected, 0 otherwise.
has_adblocker: 0
nav_type string
How the visitor got to this page.
| value | meaning |
|---|---|
navigate | a normal navigation — a link, or the address typed |
reload | the page was reloaded |
back_forward | the browser's back or forward button |
prerender | the page was preloaded by the browser before being shown |
other | a navigation type the browser reports but we do not name |
unavailable | the browser did not report one |
error | the browser API failed when we asked |
The first four are the Performance API navigation types, reported as the browser names them.
nav_type: "navigate"
screen string
Screen resolution, "width x height".
screen: "360x780"
user_agent string
The raw browser User-Agent.
user_agent: "Mozilla/5.0 (Linux; Android 14) Chrome/151.0.0.0 Mobile"
device_type string
mobile, desktop, tablet, unknown.
device_type: "mobile"
browser_name string
Browser parsed from the User-Agent. other when it cannot be determined.
browser_name: "chrome"
browser_version string
Browser version. "" when it cannot be determined.
browser_version: "151.0.0.0"
operating_system string
Operating system parsed from the User-Agent.
operating_system: "android"
operating_system_version string
Operating system version.
operating_system_version: "14"
browser_app string
The app the page opened in, when that is certain — facebook, instagram. no_app otherwise.
browser_app: "no_app"
browser_language string
Browser language.
browser_language: "ro-RO"
visitor_ip_address string
The visitor IP address.
visitor_ip_address: "86.122.24.187"
visitor_ip_country string
Country derived from the IP, ISO 3166-1 alpha-2. Describes where the request came from, which can differ from the address on the order in user.country.
visitor_ip_country: "RO"
visitor_ip_city string
City derived from the IP.
visitor_ip_city: "Brașov"
visitor_ip_region string
Region derived from the IP. Code or name, depending on the source.
visitor_ip_region: "BV"
visitor_ip_postal_code string
Postal code derived from the IP. An approximation of the area, not the customer address.
visitor_ip_postal_code: "500001"
products array
Order lines, in a unified shape — the same keys whatever platform the shop runs. Present on events that have them.
products[0] object
id string
Id of the variant sold.
id: "SKU-1024-BLACK"
parent_id string
Id of the parent product. = id when there are no variants.
parent_id: "1024"
name string
Variant name.
name: "Urban backpack 20L black"
parent_name string
Parent product name. = name when missing.
parent_name: "Urban backpack 20L"
sku string
Stock code.
sku: "SKU-1024-BLACK"
parent_sku string
Parent stock code. = sku when missing.
parent_sku: "SKU-1024"
ean string
Standard product code. "" when missing.
ean: "5901234123457"
gtin string
Standard product code. "" when missing.
gtin: "5901234123457"
mpn string
Manufacturer part number. "" when missing.
mpn: "NM-1024-BK"
brand string
Brand.
brand: "Nomad"
type string
Product type, as the shop declares it.
type: "simple"
category string
Main category.
category: "backpacks"
categories array
Every category, as {name} objects.
categories: [{ name: "backpacks" }, { name: "accessories" }]
quantity int
Units on the line. Minimum 1.
quantity: 2
price number
Final unit price, with tax, after the product discount.
price: 82.5
price × quantity is the line value, already net. Discounts in coupons below are already subtracted from price — do not subtract them again.
price_without_tax number
The same final unit price, excluding tax.
price_without_tax: 69.33
price_base number
List unit price, with tax, before the product discount. = price when there is no discount; the difference price_base − price is the discount per unit.
price_base: 87.5
price_base_without_tax number
List unit price, excluding tax.
price_base_without_tax: 73.53
coupons array
Discounts applied to this line. value is the line total, not per unit. Key absent when there are none.
These are distinct from the order-level coupons container: those apply across the whole order, are not reflected in product prices, and are subtracted from the aggregate value.
coupons: [{ name: "PROMO5", value: 10, type: "fixed", tax_included: true, tax_percent: 19 }]
List price 87.5, a 10 coupon across the whole line (5 per unit at quantity: 2), final price 82.5. Line value = 82.5 × 2 = 165 — exactly what values.products_value_with_tax shows.
currency string
Line currency.
currency: "RON"
exchange_rate number
Line exchange rate. 1 by default.
exchange_rate: 1
tax_percent number
Tax rate applied to the line.
tax_percent: 19
tax_included bool
Whether the prices above include tax.
tax_included: true
url string
Address of the variant. Key absent when the address is invalid.
url: "https://shop.example/backpack-black"
parent_url string
Address of the parent product. Key absent when the address is invalid.
parent_url: "https://shop.example/backpack"
image string
Main image. Key absent when no valid address exists.
image: "https://shop.example/img/1024.jpg"
images array
Every image. Key absent when no valid address exists.
images: ["https://shop.example/img/1024.jpg"]
stock_status string
Stock state, as the shop declares it.
stock_status: "1"
stock_exists bool
Availability, derived from stock_status.
stock_exists: true
stock_location string
Warehouse, when the shop declares it.
stock_location: "main-warehouse"
created_at int
When the product was added to the catalogue, ms Unix UTC. 0 when missing.
created_at: 1690000000000
properties object
The shop's own attributes for this line. Key absent when empty. See properties.
properties: { variant: ["black / M"] }
Keys marked key absent really are missing from the object in the situations described — unlike event, user, attribution, context, consent and values, where every key always arrives.
shipping array
Shipping methods on the order. Present only on events that have them.
shipping[0] object
id string
Shipping method id.
id: "courier"
name string
Shipping method name.
name: "Express courier"
type string
Method type, as the shop declares it.
type: "courier"
currency string
Currency of the shipping cost.
currency: "RON"
value number
Shipping cost, including tax.
value: 19.99
value_without_tax number
Shipping cost, excluding tax.
value_without_tax: 16.8
exchange_rate number
Exchange rate for this line. 1 by default.
exchange_rate: 1
payments array
Payment methods on the order. Present only on events that have them.
payments[0] object
id string
Payment method id.
id: "card"
name string
Payment method name.
name: "Card"
type string
Method type: card, cash, transfer, as the shop declares it.
type: "card"
value number
Amount paid with this method.
value: 164.99
coupons array
Order-level discounts. Product-level ones are in products[].coupons. Present only on events that have them.
coupons[0] object
id string
Coupon id or code.
id: "SUMMER10"
name string
Coupon name, as shown to the customer.
name: "SUMMER10"
type string
percent or fixed, as the shop declares it.
type: "percent"
value number
Discount value, including tax.
value: 20
value_without_tax number
Discount value, excluding tax.
value_without_tax: 16.81
user object always sent
Who the visitor is, as far as we know. Personal values leave only as SHA-256 fingerprints of the normalised value — plain values never leave our system.
id string
Customer id in the shop platform. "" for orders without an account.
id: "7"
emails_sha256 array
Email fingerprints, at most 5. Normalisation before hashing: lowercase.
The order is guaranteed — most recently discovered first. If you want a visitor's current address, it is the first in the list.
emails_sha256: ["34d338…206f", "9b7f21…41ac"]
All four fingerprint keys are always arrays, even where only one value can exist today. The type never changes, so you can iterate without checks. An empty list means we do not have the value.
phones_sha256 array
Phone fingerprints, same rules and same ordering. Normalisation before hashing: E.164 format.
phones_sha256: ["9f2a41…7b3c"]
first_name_sha256 array
Fingerprint of the first name. Normalisation: lowercase, trimmed, punctuation and inner spaces removed, diacritics and digits kept — Ștefan-Ion becomes ștefanion.
first_name_sha256: ["aa34d338…9e206"]
last_name_sha256 array
Fingerprint of the last name, same normalisation as the first name.
last_name_sha256: ["cc91f2a4…e4d3c"]
country string
Country from the order address, ISO 3166-1 alpha-2, lowercase. May differ from context.visitor_ip_country, which is derived from the IP.
country: "ro"
region string
County or region from the address. Free text, no fixed vocabulary.
region: "BV"
city string
City from the address. Free text.
city: "Brașov"
created_at int | null
When the account was created, ms Unix UTC. null for orders without an account.
created_at: 1690000000000
first_seen int
Earliest known interaction with the shop, ms Unix UTC.
It may decrease over time for the same visitor, as we learn about earlier interactions. When aggregating, take the minimum.
first_seen: 1785164079095
properties object
The shop's own attributes about this customer. See properties.
properties: { account_status: ["active"] }
cookies object
A { name: value } object with the cookies available from the visitor's browser on the shop domain.
Some cookies are not available for security reasons — they are neither collected nor altered. Cookies can also be excluded on request.
cookies: { _fbp: "fb.1.1785929520000.916422363983203244", _ga: "GA1.1.8296324256672419.1785542401" }
consent object always sent
What the visitor agreed to. When they have not chosen, the values reflect the shop's default setting.
analytics int
1 granted, 0 not granted.
analytics: 1
marketing int
1 granted, 0 not granted.
marketing: 0
id string
Id of the consent decision, when the consent platform provides one. "" otherwise.
id: "a1b2c3d4-…"
attribution object always sent
Where the visitor came from. The two lists are different readings of the same data: channels covers every channel, paid_sources only the paid platforms. Every entry carries the days we saw it, so you can apply whatever window your own model uses.
channels array
The channels the visitor arrived through, in order of first touch. Each channel appears once, however many times it was touched, with the span over which it was seen.
Order is information, not decoration: two visits with the same channels in a different order arrive as different lists. To compare which channels were touched, sort both lists first; to compare the journey, compare them as they are.
channels: [
{ channel: "paid", first_day: "2026-08-23", last_day: "2026-09-09", days_seen: 4 },
{ channel: "newsletter", first_day: "2026-09-03", last_day: "2026-09-03", days_seen: 1 }
]
| field | meaning |
|---|---|
channel | one of the values below |
first_day | the first day we have a record of this channel bringing the visitor, YYYY-MM-DD in UTC |
last_day | the last day it brought them; equal to first_day for a single touch |
days_seen | how many distinct days it brought them, first_day and last_day included |
first_day and last_day are days, deliberately. They are exact as days and cannot be narrowed to an hour, so do not build "hours since last touch" logic on them.
days_seen counts days the same way: two visits from the same channel on the same day count once. It measures how persistent a source was, not traffic volume, and it is never a click count.
The day is the UTC day, like every timestamp we send. For a shop east of UTC this means a late-night visit can be dated one day earlier than the shop's own reports show it — a visit at 01:00 in Bucharest is 2026-09-11 here and the 12th there. Convert to your timezone if you compare the two.
Every channel we still have a record of is listed, with its own days, so you apply whatever window your model uses.
Cutting the lists made the payload contradict itself: a channel and the platform that started it would report different histories, or a returning visitor would arrive with a first touch dated to their return. With the days on every entry there is nothing to cut for.
first_day is the first day we have, not the first day it happenedWe do not keep the journey forever. For a visitor who has been coming back for a long time, first_day is where our record begins, not where the relationship did.
| value | what it covers |
|---|---|
paid | advertising bought on a platform: Google, Meta, TikTok, Bing, X, Snapchat, Pinterest, LinkedIn, OpenAI, Biano, Favi |
retargeting | remarketing platforms — Criteo, RTB House |
affiliate | affiliate networks, paid on commission — 2Performant, Profitshare, Awin, Admitad, TradeDoubler, CJ, Impact, Rakuten, Webgains, Flip |
ai | AI assistants, organic — ChatGPT, Perplexity, Gemini, Claude, Copilot, DeepSeek, Grok, You.com, Poe, Mistral, Meta AI, Phind. An ad inside ChatGPT is paid, not ai |
newsletter | your own email |
organic | a search engine, with no ad click on it — Google, Bing, Yahoo, Yandex, DuckDuckGo, Baidu, Ecosia, Startpage, Qwant, Seznam |
social | a social network, with no advertising — Facebook, Instagram, TikTok, X, Snapchat, Pinterest, LinkedIn, Reddit, YouTube |
others | another site sent the visitor — any domain not named above |
others is ordinary referral traffic, not an errorIt is where a plain domain lands: a blog, a marketplace, a price comparison site, a partner's page. There is no separate referral value — a domain we do not map specially is simply others.
That also covers a search engine or a social network outside the lists above, because all we can tell is that some site sent the visitor. If a market you sell in leans on an engine or a network we are missing, tell us and we add it — until then that traffic is real, and it sits in others.
paid means advertising, not "everything that cost money": retargeting and affiliate are paid too, but appear separately because they are budgeted separately. For the paid total, take the union of the three.
An empty list — no source was identified: the visitor arrived directly, typed address or bookmark, or the signal was lost. There is no direct value; the absence is the answer.
Which platform, not just which channel. Named platforms arrive only for paid traffic, in paid_sources. For organic, social, others and newsletter you get the channel alone — not the search engine, the social network or the referring site.
That is deliberate: platform names matter for budget attribution, which is a paid concern. Everywhere else the channel is the useful unit, and sending the full source taxonomy would tie your integration to our internal classification. If you need the specific source, tell us — it is a configuration question, not a limitation of the format.
The list may grow. Treat an unknown value as a new channel, not as an error.
paid_sources array
Platform names rather than channel names, ordered by first touch like channels. [] means no paid source is on record for this visitor.
paid_sources: [
{ source: "google", type: "ads", first_day: "2026-08-23", last_day: "2026-09-09", days_seen: 4 },
{ source: "2performant", type: "affiliate", first_day: "2026-09-07", last_day: "2026-09-07", days_seen: 1 },
{ source: "criteo", type: "retargeting", first_day: "2026-09-10", last_day: "2026-09-10", days_seen: 1 }
]
| field | meaning |
|---|---|
source | the platform name |
type | ads, affiliate, retargeting, or other |
first_day | the first day we have a record of this platform bringing the visitor, YYYY-MM-DD in UTC |
last_day | the last day it brought them |
days_seen | how many distinct days it brought them, first_day and last_day included |
days_seen does not add up across the two listsA channel's days_seen is its own distinct days, not the sum of its platforms'. Google and Meta both bringing the visitor on the same day is one day for the channel paid and one day for each of them — so the platforms can add up to more than the channel. That is what "distinct days" means, not a discrepancy.
Like channels, this list is not cut — every paid platform we still have a record of is here, with its own days.
type before you sum anythingThis list is everything acquired for money, which is not the same as everything you bought as advertising. An affiliate network is billed on a sale, a retargeting DSP from a separate budget, and search ads per click — grouping them under one total answers no real question. type is what lets you separate them.
values object always sent
Order totals, in event.currency, at most four decimals. The event value itself is in event.value.
products_count int
Distinct product lines.
products_count: 1
products_total_quantity int
Units in total, across every line.
products_total_quantity: 2
products_value_with_tax number
Sum of the lines — unit price × quantity — including tax.
products_value_with_tax: 165
products_value_without_tax number
The same sum, excluding tax.
products_value_without_tax: 138.66
shipping_count int
Shipping methods on the order.
shipping_count: 1
shipping_value_with_tax number
Shipping cost, including tax.
shipping_value_with_tax: 19.99
shipping_value_without_tax number
Shipping cost, excluding tax.
shipping_value_without_tax: 16.8
coupons_count int
Order-level discounts applied. Product-level discounts are already reflected in product prices and are not counted here.
coupons_count: 1
coupons_value_with_tax number
Value of the order-level discounts, including tax.
coupons_value_with_tax: 20
coupons_value_without_tax number
Value of the order-level discounts, excluding tax.
coupons_value_without_tax: 16.81
payments_count int
Payment methods used.
payments_count: 1
payments_total_value number
Sum of the payments.
payments_total_value: 164.99
The net is products_value_without_tax + shipping_value_without_tax - coupons_value_without_tax.
queries array
Marketing parameters from the addresses visited: utm_campaign, utm_source, utm_medium, utm_content, utm_term, and the click identifiers of the active advertising platforms — fbclid, gclid, ttclid and so on, once configured for the shop.
One entry per parameter, with the last value seen — not a history of every value. The list includes parameters from earlier visits, not only the current one.
Unlike the attribution lists, this one is cut — a click id kept forever would grow without bound. The window is at least 30 days, and wider when the shop's attribution window is wider than that. It is measured against event.timestamp, so a webhook that arrives late carries the same list it would have carried at the time of the event.
You do not need the exact number to use the data: every entry carries its own added_at, so what is in the list tells you what was in the window.
queries[0] object
name string
Parameter name.
name: "utm_source"
value string
Parameter value.
value: "facebook"
added_at int
When we last saw this value, ms Unix UTC.
added_at: 1785929520000
properties
event.properties, user.properties and products[].properties are free-form: the content is exactly what the source — the shop or its platform — sends. We do not add keys, translate them, filter them by meaning, or interpret them.
The only thing we do is bring them to a predictable shape, so they do not differ from platform to platform:
| in | out |
|---|---|
"Color Variant" | "color_variant" — keys become snake_case |
"black" | ["black"] — values are always arrays |
42, true | ["42"], ["true"] — numbers and booleans become text |
["red", "red", "blue"] | ["red", "blue"] — duplicates removed |
"", null, undefined | ignored |
{ "nested": "obj" } | ignored — if it is the only value, the key disappears |
So the keys and values are the source's; the shape is ours. A key reaches you only if it has at least one value left after the cleanup — you will never receive an empty array.
There is no fixed key list here and there cannot be: every shop sends something different. Nothing in this container is guaranteed — a key may appear or disappear without notice, because the decision is the shop's, not ours. Read it as data, not as something you can build a schema on.
Time windows
Three containers carry history; everything else describes the current event.
| container | how far back |
|---|---|
attribution.channels | everything we still have, each entry dated |
attribution.paid_sources | the same, it is the same data read differently |
queries | at least 30 days, wider if the shop's attribution window is |
The two attribution lists are not cut: they carry their days per entry, so you apply the window your model needs. queries is cut, because a click id kept without a window would grow without bound, and it is measured against event.timestamp, not delivery time — a webhook that arrives late produces the same payload it would have produced at the time of the event.
One limit applies to all three: we do not keep a visitor's history forever, so the oldest day you can see is bounded by how long we hold it, not by any window.
Full example
A single checkout_completed, with every container populated.
{
"event": {
"name": "checkout_completed",
"id": "13195238211910",
"unique_id": "13195238211910",
"deduplication_id": "idx13195238211910",
"timestamp": 1786202400000,
"value": 164.99,
"currency": "RON",
"exchange_rate": 1,
"reason": null,
"properties": {}
},
"context": {
"url": "https://shop.example/checkout",
"referrer_url": "",
"landing_url": "https://shop.example/lp?utm_source=facebook",
"timestamp": 1786202390000,
"last_ad_source": "facebook",
"integration": "browser|webhook merchantpro",
"data_source": "website",
"script_id": "YOURSCR1",
"page_type": "checkout",
"has_adblocker": 0,
"nav_type": "navigate",
"screen": "360x780",
"user_agent": "Mozilla/5.0 (Linux; Android 14) Chrome/151.0.0.0 Mobile",
"device_type": "mobile",
"browser_name": "chrome",
"browser_version": "151.0.0.0",
"operating_system": "android",
"operating_system_version": "14",
"browser_app": "no_app",
"browser_language": "ro-RO",
"visitor_ip_address": "86.122.24.187",
"visitor_ip_country": "RO",
"visitor_ip_city": "Brașov",
"visitor_ip_region": "BV",
"visitor_ip_postal_code": "500001"
},
"products": [
{
"id": "SKU-1024-BLACK",
"quantity": 2,
"price": 82.5,
"price_base": 87.5
}
],
"shipping": [
{
"id": "courier",
"value": 19.99
}
],
"payments": [
{
"id": "card",
"type": "card",
"value": 164.99
}
],
"coupons": [
{
"id": "SUMMER10",
"type": "percent",
"value": 20
}
],
"user": {
"id": "7",
"first_name_sha256": [
"aa34d338…9e206"
],
"last_name_sha256": [
"cc91f2a4…e4d3c"
],
"emails_sha256": [
"34d338…206f"
],
"phones_sha256": [
"9f2a41…7b3c"
],
"country": "ro",
"region": "BV",
"city": "Brașov",
"created_at": 1690000000000,
"first_seen": 1785164079095,
"properties": {
"account_status": [
"active"
]
}
},
"cookies": {
"_fbp": "fb.1.1785929520000.916422363983203244"
},
"consent": {
"analytics": 1,
"marketing": 0,
"id": ""
},
"attribution": {
"channels": [
{ "channel": "paid", "first_day": "2026-08-23", "last_day": "2026-09-11", "days_seen": 6 },
{ "channel": "newsletter", "first_day": "2026-09-03", "last_day": "2026-09-03", "days_seen": 1 },
{ "channel": "organic", "first_day": "2026-09-06", "last_day": "2026-09-08", "days_seen": 2 },
{ "channel": "retargeting", "first_day": "2026-09-10", "last_day": "2026-09-10", "days_seen": 1 },
{ "channel": "affiliate", "first_day": "2026-09-11", "last_day": "2026-09-11", "days_seen": 1 }
],
"paid_sources": [
{ "source": "facebook", "type": "ads", "first_day": "2026-08-23", "last_day": "2026-09-09", "days_seen": 4 },
{ "source": "google", "type": "ads", "first_day": "2026-08-30", "last_day": "2026-09-11", "days_seen": 3 },
{ "source": "criteo", "type": "retargeting", "first_day": "2026-09-10", "last_day": "2026-09-10", "days_seen": 1 },
{ "source": "2performant", "type": "affiliate", "first_day": "2026-09-11", "last_day": "2026-09-11", "days_seen": 1 }
]
},
"values": {
"products_count": 1,
"products_total_quantity": 2,
"products_value_with_tax": 165,
"products_value_without_tax": 138.66,
"shipping_count": 1,
"shipping_value_with_tax": 19.99,
"shipping_value_without_tax": 16.8,
"coupons_count": 1,
"coupons_value_with_tax": 20,
"coupons_value_without_tax": 16.81,
"payments_count": 1,
"payments_total_value": 164.99
},
"queries": [
{
"name": "utm_source",
"value": "facebook",
"added_at": 1785929520000
}
]
}