Event Payload Contract
Universal TrackStackEvent shape for SDK capture and edge ingestion.
Every event — canonical or extension — follows one payload contract. The edge normalizes flat SDK payloads into userData + customData before CAPI delivery.
TrackStackEvent type
type TrackStackEvent = {
/** Canonical or PascalCase extension event name */
event: string;
/** Globally unique ID for dedup across retries and platforms */
eventId: string;
/** ISO timestamp (optional — edge adds if missing) */
ts?: number;
// Identity (hashed SHA-256 at edge for ad platforms)
email?: string;
phone?: string;
firstName?: string;
lastName?: string;
city?: string;
state?: string;
zip?: string;
country?: string;
externalId?: string;
// Revenue
value?: number;
currency?: string;
transactionId?: string;
orderId?: string;
plan?: string;
previousPlan?: string;
subscriptionId?: string;
refundId?: string;
// B2B context
leadId?: string;
companyId?: string;
companyName?: string;
companyDomain?: string;
accountName?: string;
crmId?: string;
opportunityId?: string;
opportunityStage?: string;
pipelineStage?: string;
salesOwner?: string;
// Content / navigation
pageUrl?: string;
pageTitle?: string;
referrer?: string;
contentType?: string;
contentId?: string;
contentName?: string;
contentCategory?: string;
contentIds?: string[];
feature?: string;
demoType?: string;
actionType?: string;
paymentMethod?: string;
leadEventSource?: string;
numItems?: number;
/** Per-platform overrides (conversion rule IDs, sendTo, etc.) */
platformParams?: Record<string, Record<string, unknown>>;
};
Edge request shape
The SDK POSTs batched events to /events:
{
"events": [
{
"event": "Purchase",
"eventId": "evt_123",
"email": "jane@example.com",
"value": 1299,
"currency": "USD",
"transactionId": "txn_123",
"plan": "Growth",
"companyDomain": "acme.com",
"platformParams": {
"linkedin": { "conversionRuleId": "12345678" }
}
}
]
}
Alternatively, use nested userData / customData — the edge accepts both flat and nested shapes.
Identifier priority
TrackStack resolves identity in this order for ad-platform matching:
- Click IDs —
fbclid,gclid,li_fat_id,ttclid, etc. (from cookies / URL, forwarded by SDK) - Hashed PII — SHA-256 email, phone, name, location at edge
- External ID — Your
userId/customerId(hashed at edge) - B2B company context —
companyDomain,companyName,companyIdfor account-based routing and enrichment
Always send email when available. For B2B, include companyDomain on lead and revenue events.
Required fields by event family
| Family | Required fields |
|---|---|
| Page views | pageUrl recommended |
| Content | contentType |
| Leads | email |
| SignUp | email |
| StartTrial | plan |
| FirstAction | actionType |
| Purchase / upgrades / renewals | value, currency, transactionId |
| Refund / chargeback | refundId or transactionId, value, currency |
Validation runs at the edge against canonical event definitions. Extension events skip strict required-field checks.
Dedup (eventId)
Provide eventId for deterministic dedup when:
- Retrying failed captures
- Sending the same conversion from client and server
- Matching pixel + CAPI pairs
Platform dedup windows:
| Platform | Dedup field | Window |
|---|---|---|
| Meta | event_id | 48 hours |
transaction_id | 24 hours | |
event_id | 48 hours | |
| Snapchat | event_id | 48 hours |
| TikTok | event_id | Per Events API spec |
click_id / event_id | Per account config |
Quality scoring
Events are scored at the edge on:
- Identity completeness (email + click ID + externalId)
- Required fields for the canonical event
- Value/currency presence on revenue events
- Duplicate
eventIdwithin dedup window
Low-quality events may still deliver but appear with warnings in delivery logs.
Related docs
- B2B SaaS events — Full taxonomy
- Routing policy — Where events go
- SDK setup — Client integration