Skip to content

TrustAnalytics API

The server API takes events that only your backend knows for sure: an order your payment provider confirmed, a refund, a signup confirmed by e-mail, an error in a background job. They land in the same website as the browser events, and when you pass the browser’s session id, in the same visit.

With the same key you read the statistics of the project’s websites for reports, dashboards or a spreadsheet of your own.

Base path: https://api.trustcomponent.com/analytics/v1 Auth: Authorization: Bearer ak_… (a Project API key)

Create an API key under Settings › API Keys & Webhooks of the project that holds the Analytics module, and keep it on your server — never in the browser. The key names the project; every batch and every statistics request names the website by its website id, shown in the website settings next to the site key and listed by GET /websites. A website of another project answers 404.

The browser SDK uses the public site key instead and needs no API key.

Request
curl -X POST "https://api.trustcomponent.com/analytics/v1/server-events/batches" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ak_your_key_here" \
-d '{
"websiteId": "cc2e2d5e-d1ef-4a7f-a7bd-dec5b37df47a",
"sentAt": "2026-11-02T10:30:00.000Z",
"hasConsent": true,
"release": "2026.11.1",
"events": [
{
"id": "5f0e6a1c-2b8d-4c1e-9a7f-3d2b1c0e9f8a",
"occurredAt": "2026-11-02T10:29:58.000Z",
"name": "signup_confirmed",
"category": "CUSTOM_EVENTS",
"userId": "user-4711",
"properties": { "plan": "pro" }
}
]
}'

Batch:

FieldRequiredDescription
websiteIdYesWebsite the events belong to
sentAtYesWhen your server sent the batch; used to correct clock drift
hasConsentYesWhether the end users of this batch consented; applies to every event without its own hasConsent, see Consent
releaseNoVersion of your backend, to compare errors between releases (up to 100 characters)
eventsYes1 to 50 events, in the order they happened

Event:

FieldRequiredDescription
idYesA string of 1 to 100 characters, unique within the website and compared exactly: a UUID, or the id the event already has in your system (an order id, the id of a payment provider’s refund or webhook event). A retried batch carries the same ids, see Retries
occurredAtYesWhen it happened; events older than 48 hours are dropped as STALE
nameYesEvent name (up to 100 characters). purchase, refund, error and identify are reserved
categoryYesCategory of the event, e.g. CUSTOM_EVENTS, COMMERCE, ERRORS, IDENTITY
pageViewIdNoThe page load in the browser the event belongs to, see Link to visits
userIdIf no pageViewIdId of the user in your system — never an e-mail address (up to 200 characters). Not needed for a refund, which its order id identifies
urlNoPage the event relates to; only hostname, path, the query parameters the website’s filter keeps and campaign parameters are kept
pagePatternNoRoute of the page with its variables written as :name or *, e.g. /orders/:orderId/complete; groups the page in reports and is ignored when it does not match the path of url
ipAddressNoAddress of the end user, for country and rules only, never stored
userAgentNoUser agent of the end user, for browser and device only, never stored
hasConsentNoWhether the end user of this event consented; takes precedence over hasConsent of the batch, which applies when it is left out, see Consent
propertiesNoUp to 50 string values (up to 500 characters each); for identify these are the user properties
commerceFor purchaseSee Purchases and refunds
refundFor refundSee Purchases and refunds
errorFor category ERRORStype, message (up to 500), stack (up to 4000), source

The address and user agent of the request itself belong to your server and are never used for geo, device or bot detection. Without ipAddress and userAgent, country and device stay unknown.

Browser-only data is refused with BROWSER_ONLY: pageview and pageleave, and the categories WEB_VITALS, NETWORK_DIAGNOSTICS, CONSOLE_DIAGNOSTICS and HEATMAPS.

A server has no consent banner, so the batch says whether the end user agreed: hasConsent is true when your own consent records hold a consent for the users in this batch. An event can say it for its own user with hasConsent on the event, which takes precedence over the batch; an event without it follows the batch. The website’s collection modes decide what that means: an event of a group on Only with consent without consent is dropped as CONSENT, a group on Always is stored either way.

On top of that, the website’s switches, your plan and your rules apply as for browser events — a rule with the condition Source › Server applies only to server events. You vouch for the consent with the batch and with every event that states its own.

The userId of a server event, and a server identify with user properties, need Recognition and cookie to be collected for the event — on Always, or on Only with consent when the event or, without its own value, the batch says hasConsent: true. Otherwise the event is kept without the user id and gets a session of its own unless it names a pageViewId.

Events of users with different consent can share one batch: set hasConsent on the events whose user differs from the batch value.

  • With pageViewId the event joins the visit of that page load — its source, campaign, landing page and device. Read the id in the browser with trustanalytics.getPageViewId() and send it to your backend with the order or form, e.g. as a hidden field or request property.
  • Without pageViewId the event joins the latest session of the same userId within the last 30 minutes. If there is none, it opens a server session without a page (shown as (server)) and counts as Unattributed in source reports.
  • Either way, the event carries the source Server, which you can filter by in every report (Sent from).
Purchase event
{
"id": "9d1c…",
"occurredAt": "2026-11-02T10:29:58.000Z",
"name": "purchase",
"category": "COMMERCE",
"pageViewId": "1b6f…",
"commerce": {
"orderId": "order-10045",
"amountMinor": 5490,
"taxMinor": 877,
"shippingMinor": 490,
"discountMinor": 490,
"coupon": "AUTUMN10",
"currency": "EUR",
"products": [
{ "productId": "sku-2041", "name": "Trail running shoe", "quantity": 2, "unitPriceMinor": 2495 },
{ "productId": "sku-0007", "unitPriceMinor": 500 }
]
}
}
Refund event
{
"id": "4a7e…",
"occurredAt": "2026-11-05T08:12:00.000Z",
"name": "refund",
"category": "COMMERCE",
"userId": "user-4711",
"refund": { "orderId": "order-10045", "amountMinor": 2495 }
}
  • Both use the category COMMERCE; amounts are in the minor unit of the order currency (cents for EUR).
  • commerce: orderId (up to 100 characters, unique per website), amountMinor, currency (ISO 4217), optional itemCount and up to 50 products with productId, unitPriceMinor and optional name, category, quantity.
  • Tax, shipping, discount, coupon: optional taxMinor and shippingMinor (the parts of amountMinor that are tax and shipping), discountMinor (what was already taken off amountMinor, as a positive number) and coupon (the code the customer redeemed, up to 100 characters) are stored with the order as your shop reports them. amountMinor stays the total paid and is never computed from them. The three amounts lie between 0 and 1000000000000 like amountMinor; a purchase with one outside that range is dropped as MALFORMED.
  • Browser and server: a server purchase replaces a browser purchase with the same order id, a browser purchase never replaces a server one. Without products, the order lines of the browser purchase are kept, and so are its tax, shipping, discount and coupon wherever the server purchase leaves one out. The server event itself is not stored again and is reported as ORDER_CONFIRMED. A second server purchase with the same order id is dropped as DUPLICATE_ORDER.
  • Refunds need the order to be known to the website — otherwise they are dropped as UNKNOWN_ORDER. A refund is counted once per event id, so send the id of the refund (or of the payment provider’s refund) as the id of the event. Several partial refunds add up, never beyond the order total. Revenue in the reports is net of refunds.
CodeMeaning
202Batch processed — the body lists what was stored and what was dropped
400Batch malformed, or more than 50 events
401API key missing or unknown
403Tracking suspended, because the project is locked or its trial has expired
404No website with this id in the project of the API key
413Request body larger than 1 MB
429Too many batches for this API key — wait the seconds in Retry-After and retry
Response (202)
{
"accepted": 48,
"dropped": [
{ "reason": "DUPLICATE", "count": 1 },
{ "reason": "CONSENT", "count": 1 }
]
}

A batch is never refused because of single events: events that are not allowed are dropped one by one, and dropped lists each reason that occurred with its count — MALFORMED, STALE, PRIVACY_SIGNAL, CONSENT, PLAN, RULE, DUPLICATE, DUPLICATE_ORDER, ORDER_CONFIRMED, UNKNOWN_ORDER, BROWSER_ONLY, ERASED (HOSTNAME and BOT apply to browser events only). They also appear under Settings › Data collection & privacy › Last 24 hours of the website, see Dropped events.

Generate each event’s id once and store it with the event (or derive it from your own id, e.g. a UUID v5 of the order id for a purchase; a refund uses its own id, never the order id, or the second partial refund of an order is dropped as DUPLICATE). On a timeout, 429 or 5xx, send the same batch again (after a 429 once the seconds in its Retry-After header have passed): events that were already stored are counted as DUPLICATE, never twice. Retry within 48 hours, after that the events are STALE.

Request
curl "https://api.trustcomponent.com/analytics/v1/websites/cc2e2d5e-d1ef-4a7f-a7bd-dec5b37df47a/stats/breakdowns?dimension=PATH&from=2026-10-01T00:00:00.000Z&to=2026-11-01T00:00:00.000Z&filter=COUNTRY:IS:DE&pageSize=10" \
-H "Authorization: Bearer ak_your_key_here"
EndpointReturns
GET /websitesThe websites of the project with id, name, hostnames and time zone, paged with page and pageSize (up to 1,000, 100 by default)
GET /websites/{id}/stats/overviewPageviews, sessions, visitors, events, bounces and engaged time of the range and of the period before it
GET /websites/{id}/stats/timeseriesThe same figures per hour or per day (granularity=HOUR or DAY), empty buckets as zero, at most 1,000 points
GET /websites/{id}/stats/breakdownsThe figures split by one dimension — PATH, ENTRY_PATH, REFERRER_SOURCE, UTM_CAMPAIGN, COUNTRY, BROWSER, EVENT_NAME and more — paged with page and pageSize (up to 1,000)
GET /websites/{id}/stats/conversionsConversions, conversion rate and revenue of every goal
GET /websites/{id}/realtimeSessions active in the last five minutes, their pages and the pageviews per minute
  • Range: from (inclusive) and to (exclusive) as UTC timestamps, both required. Days start at midnight in the time zone of the website.
  • Filters: filter=dimension:operator:value, repeatable up to 20 times — for example PATH:STARTS_WITH:/blog, UTM_SOURCE:IS:newsletter or BOUNCE:IS:false. Operators are IS, IS_NOT, CONTAINS, NOT_CONTAINS, STARTS_WITH, ENDS_WITH and MATCHES (a regular expression). Filters on different dimensions must all match, filters on the same dimension are alternatives.
  • Match mode: only GET /websites/{id}/stats/conversions takes filterMatchMode: ANY counts a session that matches any one of the filters instead of all of them, ALL is the default. The other endpoints always require all filters on different dimensions to match.
  • Retention: without a filter the figures come from the aggregates and reach back as far as those are kept; with a filter they are counted from the stored sessions and events, as in Explore. A range beyond that answers 400. Beyond the stored sessions, visitors is estimated from the hourly aggregates (within about 1 %).
  • Limits: 60 requests per minute and API key; beyond that 429 with a Retry-After header that names the seconds until the next request is accepted. All plans include the statistics API. While the project is locked or its trial has expired, the statistics answer 403; GET /websites still lists the websites.