Skip to main content

Send events through the HTTP API

Request contract​

Copy the ingestion endpoint from Tracking setup for your installation. The route is POST /ingest/v1/events. Send an event object or a direct array of event objects with Content-Type: application/json. Do not wrap a batch in an events property unless that property is part of one event's own schema.

HeaderValue
AuthorizationBearer <ingestion-key>
Content-Typeapplication/json
X-DDA-Event-Schema-VersionPublished positive integer version from Update tracking

The key selects the organization, project and environment. Payload fields cannot change that scope. One schema version applies to every event in a request. Omit the version header only for discovery before the project's first published version. After publication, a missing version is an error. Keep the sender pinned to the version it implements until you intentionally update it.

Optional debug headers​

To receive detailed validation feedback while implementing tracking, add these headers alongside the normal request headers:

SenderAdditional headers
Server key, including Server-GTMX-DDA-Debug: true
Browser keyX-DDA-Debug: true and X-DDA-Debug-Token: <debug-token-for-this-browser-key>

Use the literal value true. The browser debug token is separate from the ingestion key and is available under Ingestion keys for that exact browser key. A server key needs no additional debug token.

Send an event and inspect results[].validation.status. With debug authorized, results[].validation.issues adds field-level details for warnings and errors. Debug does not change event acceptance or delivery. A missing or invalid browser debug token leaves the compact response in place.

See Debug Mode and validation feedback for complete header examples, reading the response and returning validation through Server-GTM to the browser console.

Define and map your payload​

Platform accepts your JSON structure. The published schema defines required fields, types and mappings. There is no fixed field name that automatically gives a value its meaning. For example:

{
"event": {
"name": "button_click",
"id": "example-event-0001",
"timestamp": "2026-10-11T12:00:00Z"
},
"button": {"id": "start_trial"}
}

For this example, map event.name to Event name, event.id to Producer event ID, and event.timestamp to the event timestamp. Include button.id as a string field for button_click. Use a fresh timestamp and a unique ID for each real event. Add all fields required by your published Base Schema and event-specific schema. Producer event ID is optional, but a stable ID is important for safe retries.

Unknown fields produce warnings and are removed before persistence. Do not rely on undeclared fields reaching your warehouse. Missing required fields and type errors reject the affected event. Review schema changes before updating your sender.

Send from a server​

Store the server key in your server's secret configuration. The following command assumes INGESTION_ENDPOINT, INGESTION_KEY and SCHEMA_VERSION are set securely, and event.json contains an event matching that published schema:

curl --request POST "$INGESTION_ENDPOINT" \
--header "Authorization: Bearer $INGESTION_KEY" \
--header 'Content-Type: application/json' \
--header "X-DDA-Event-Schema-Version: $SCHEMA_VERSION" \
--data-binary @event.json

Inspect both the HTTP status and the JSON body. Avoid logging the Authorization header. For a relay, send the original visitor context in the fields mapped by your schema where needed. The relay's own HTTP User-Agent is not a substitute for the event's mapped visitor User-Agent, for example for bot detection.

Configure JSON Server Tag in Server-GTM​

For the usual browser-to-server setup, JSON Tag sends from the browser to your tagging server. JSON Client runs inside Server-GTM, receives the request and prepares Event Data. JSON Server Tag then forwards that data to Platform.

Choose the server-side JSON Server Tag method in Tracking setup. In your server container, configure its HTTP Endpoint, POST method, JSON content type and the headers above under Custom Request Headers. Under Map Event Properties, map your incoming event variables to the JSON structure described by your schema.

Choose the trigger for the events you intend to forward. In Preview, inspect the outgoing request body and response. After publishing a new schema, use Update tracking to update its header in the tag and publish the container. Avoid forwarding an event both through a browser integration and a relay unless you have deliberately designed their event IDs and deduplication behavior.

For development feedback in the browser, enable Debug Mode and JSON Client response forwarding.

Send directly from a browser​

Use a browser key and an allowed origin. The runtime in the tracking guide provides the generated integration. A custom sender uses the same HTTP contract:

async function sendEvent(endpoint, browserKey, schemaVersion, event) {
const response = await fetch(endpoint, {
method: 'POST',
credentials: 'omit',
headers: {
'Authorization': `Bearer ${browserKey}`,
'Content-Type': 'application/json',
'X-DDA-Event-Schema-Version': String(schemaVersion)
},
body: JSON.stringify(event)
})
const result = await response.json()
return {httpStatus: response.status, ...result}
}

The browser performs a CORS preflight when needed. A successful preflight does not confirm that the key is authorized for that origin. The authenticated POST performs that check. Do not use sendBeacon for this API because it requires an Authorization header. Browser cookies are not used to authenticate the ingestion request.

Read results for every event​

A single event and a batch use the same response structure:

{
"requestId": "example-request-id",
"results": [
{"index": 0, "status": "queued", "validation": {"status": "passed"}},
{"index": 1, "status": "rejected", "code": "platform_ingestion_event_invalid", "validation": {"status": "failed"}}
]
}

index is zero-based and refers to the original input order. A successful HTTP status can contain rejected events. Read every result, including when HTTP is 200 or 202. A request-wide failure instead returns requestId, status, code and message. See the result and error guide for interpretation.

Retries and batching​

Keep the same mapped Producer event ID and event content when retrying a request whose outcome is unknown. Within the deduplication retention window, an identical replay is duplicate. Reusing the ID with conflicting content is rejected. Without a mapped and supplied Producer event ID, regular events are not deduplicated. Do not promise exactly-once delivery based on retries alone.

For partially accepted batches, correct and resend rejected events individually. Do not blindly resend all events after a successful response. For network failures or temporary server errors, use bounded retries with backoff and stable IDs. A client timeout does not prove that the server rejected or rolled back the request. Respect Retry-After when provided. Authentication and schema errors require a configuration or payload fix instead of an unchanged retry loop.

Default request limits are 100 events per batch, 32 KiB per event and 256 KiB per request. Deployments may configure different limits. Keep batches below both count and byte limits. Organization quotas and rate limits also apply.