This endpoint is enabled per tag. Ask your Thrad contact to issue an
events:write key for the tag IDs your service may send to. Keep the key on your server; never expose it in browser or mobile code.Quickstart
The Thrad Tag captures browser activity and attribution context. For confirmed purchases, signups, subscriptions, and CRM events, send the conversion from your backend as well:- More reliable: server requests are not affected by ad blockers or browser lifecycle failures.
- Richer: attach the order, line items, email, phone, or customer ID your frontend does not have.
- Deduplicated: reuse
event_idacross browser and server copies of the same conversion. - Flexible: send offline, phone, app, and delayed lifecycle events.
How it works
- Install the Thrad Tag on your site. It captures consent-permitted Thrad and OpenAI attribution context.
- At conversion, pass
window.thradTag.getServerContext()from the browser to your backend with the order or lead details. - Your backend sends one event to Thrad. We record it once and route it to Thrad and/or enabled OpenAI measurement without the advertiser choosing a provider endpoint.
events:write key do not by themselves turn on OpenAI forwarding; Thrad enables it only for an approved tag after the integration is ready. Your request format stays the same whether the event is used for Thrad attribution, OpenAI measurement, or both.
Reference guides
- Authentication explains how to obtain and use an
events:writekey. - Identity and consent covers hashed and plaintext matching data, privacy, and routing.
- Testing covers
test_modeand common integration errors. - Event reference lists event names, data types, and response outcomes.
Step 1 — Install the Tag
Install the Thrad Tag on every page. The server integration also works for offline events without a browser context, but using the Tag improves attribution for web conversions.Step 2 — Read context on conversion
On a confirmation page or in your checkout client, read the context and send it to your own backend unchanged. It is safe to call after the Tag loads. It contains no API key or raw matching identity.Step 3 — Send the event from your backend
Endpoint and authentication
events:write capability and an allowlist of tag IDs. A key cannot submit an event for another advertiser’s tag.
Use an
events:write key issued for the tag. Keep it server-side only. Send a stable event ID on every event and reuse it for retries and any matching browser event.
web events require source_url.
Envelope and results
An envelope hasversion: 1, one required tag_id, optional test_mode, and between one and 1,000 events. Unknown fields are rejected.
accepted, duplicate, suppressed, invalid, and, in test mode, validated. An invalid event has one or more machine-readable errors; duplicate and suppressed events can include warnings. Retry only events whose outcome is not known (503), using their original event_id.
Set test_mode: true to validate the envelope and every event without creating a canonical record or forwarding to OpenAI. This is the safe integration check before sending live traffic.
Events and data
Use these canonical event names:page_viewed, contents_viewed, items_added, checkout_started, order_created, lead_created, registration_completed, appointment_scheduled, subscription_created, trial_started, app_installed, app_opened, and custom.
Common aliases such as purchase, add_to_cart, page_view, and signup are normalized to their canonical names. custom requires custom_event_name. app_installed and app_opened require action_source: "mobile_app" and data.type: "customer_action".
data.type must match the event:
Identity and OpenAI advanced matching
Pre-hashed email and phone are the recommended default. Send lowercase 64-character SHA-256 values inemails_sha256 and phone_numbers_sha256; include both whenever you collected them and consent permits it. Thrad also accepts external_ids_sha256, first_names_sha256, and last_names_sha256. Each field accepts up to three unique values; geographic fields (countries, cities, regions, postal_codes) remain raw.
For a simpler first integration, Thrad can hash plaintext emails, phone_numbers, external_ids, first_names, and last_names at ingress. Plaintext is normalized and discarded before logs, errors, canonical records, or queues are created. Prefer pre-hashing when it is easy to do in your service.
For example, this is the convenience form of the hashed identity in the request above:
data.custom_fields or any other field. Use either form only when measurement consent is present. Thrad merges supplied hashes and locally generated hashes, deduplicates them, and retains at most three values per identity field.
Normalize before hashing exactly as follows:
- email: trim and lowercase;
- phone: convert to international digits-only form, remove
+or00and punctuation, then keep 8–15 digits; - external ID: trim only; preserve case;
- first and last name: lowercase and remove whitespace and ASCII punctuation; preserve non-ASCII characters.
Dependency-free hashing examples
Node.js:0. If your runtime cannot lowercase non-ASCII names consistently, use pre-hashed name values instead of relying on the plaintext convenience path.
Use context.oppref unchanged when your server captured it from the Thrad/OpenAI browser flow. context.oppcref is an unchanged lowercase UUIDv4 and is valid only for page-view, item-added, checkout-started, and order-created events. Send user.obref unchanged when available and permitted by measurement consent.
Consent, routing, and deduplication
consent.measurement and consent.personalization default to true. Set measurement: false when the user has not consented to measurement. The event is returned as suppressed and is neither recorded nor forwarded.
The same canonical event can route to Thrad, OpenAI, or both. A signed Thrad click context (click_id, click_exp, and click_sig) enables Thrad attribution. An OpenAI reference (oppref, oppcref, or obref) or a supported advanced-matching identifier enables OpenAI eligibility. Your advertiser-visible API response does not expose provider credentials or routing internals.
Using the Browser Tag and Server-to-server CAPI together
When the same purchase is reported by the updated Browser Tag or Shopify Custom Pixel and this API, the server must use the browser’s canonical purchase identity:gid://shopify/Order/<positive numeric ID>
to its numeric suffix; the equivalent numeric string uses that same suffix.
Preserve other order IDs unchanged. JSON contains two strings, no spaces and
literal Unicode. Do not use a cart/checkout token, callback ID, amount or timestamp
as order identity. Different tag IDs produce different purchase identities.
Node.js example for your server’s finalized order:
event_id in the existing /v1/api/events
envelope, and reuse it for retries. The updated browser can omit event_id and
derive the same value; an explicitly supplied browser ID must match. This S2S
schema does not accept an added order_id field. Thrad’s internal purchase-identity
reservation endpoint is service-only and is not an advertiser API.
Thrad must confirm release availability and reconcile existing purchase producers
before cutover. A hosted pixel update cannot change your server’s existing random
ID generator or recover historical order mappings. Preserve a working server
integration while those identities are checked.
Independent S2S events and conversions outside the updated browser purchase path
can retain their valid stable IDs. Do not change unrelated IDs or generate a new
ID for a retry. Thrad deduplicates per tag_id and event_id; matching provider
delivery is checked separately from a canonical receipt.
Troubleshooting
invalid_timestamp: correct the system clock or send the event within the supported time window.invalid_identity: send valid lowercase SHA-256 hex values, or send supported plaintext arrays for local hashing.invalid_context: do not alter click signatures,oppref,oppcref, orobref.no_attribution_signal: include a signed Thrad context, an OpenAI reference, or a supported matching identifier.measurement_consent_denied: do not retry until measurement consent is granted for a new event.duplicate_event: the original request was already accepted; do not create a new ID for a retry.
