View this page as Markdown • #docs #ingest #overview
Ingest API v3
Use the TelemetryDeck Ingest API v3 to send flat events to TelemetryDeck
Usually you’ll send events to TelemetryDeck using one of our SDKs. However, if you’re working with a language or framework that we don’t have an SDK for, you can send events directly to our Ingest API.
Ingest API v3 is the newest version of the API. It differs from v2 in two ways:
- Events are flat. There is no
payloadobject. Every parameter sits at the top level of the event. - Values keep their JSON type. A number stays a number, and a boolean stays a boolean.
The v2 API stays available.
Endpoints
The v3 API has two endpoints. Both live on https://nom.telemetrydeck.com.
| Endpoint | Purpose | Request body |
|---|---|---|
POST /v3/{namespace}/ | Events from apps, games, and servers | An array of events |
POST /v3/w/ | Website events, as sent by the Web SDK | A single event object |
Send the headers Content-Type: application/json and charset=utf-8. The trailing slash is optional. The path segment w is reserved for the web endpoint, so you cannot use w as a namespace.
Events Endpoint
Send your events as a POST request to https://nom.telemetrydeck.com/v3/{namespace}/.
Replace {namespace} with your organization’s TelemetryDeck namespace. You can find your namespace in the TelemetryDeck Dashboard. The namespace is required in v3. There is no endpoint without one.
Here’s an example in cURL:
curl -X "POST" "https://nom.telemetrydeck.com/v3/your-namespace/" \
-H 'Content-Type: application/json; charset=utf-8' \
-d $'[
{
"appID": "AAAA-BBBBBBBB-CCCC-DDDD",
"clientUser": "myClientUserHash",
"receivedAt": "2026-09-24T10:15:00+00:00",
"type": "Actions.LoginView.loginButtonSucceeded",
"sessionID": "mySessionID",
"isTestMode": "false",
"floatValue": 3.14159,
"TelemetryDeck.RunContext.locale": "en_US",
"TelemetryDeck.Device.architecture": "x86_64"
}
]'Body Structure
The post body must be an array of JSON objects, because you can send multiple events at once. Each event is a flat object. Required properties, optional properties, and your own parameters all sit at the top level.
Each event must have these properties. All three must be strings.
| Property | Type | Description |
|---|---|---|
appID | string | Your app’s ID. We recommend always setting it. The server accepts an empty string only because the namespace in the URL already identifies your organization. |
clientUser | string | An identifier for the user, for example a hash of their user ID. This should always be the same for the same user. The server salts and hashes this value with SHA256 before it stores the event. |
receivedAt | string | The date and time at which the event happened, as an ISO 8601 string with time zone, for example 2026-09-24T10:15:00+00:00. Unlike v2, the server stores this value as sent and does not move it into the last 24 hours. |
These properties are optional:
| Property | Type | Description |
|---|---|---|
type | string | The type of event. The server accepts an event without a type, but the dashboard needs one, so always send it. We recommend structuring your event names in scopes separated by dots, with the event type beginning with a lowercase letter and any scope beginning with an uppercase letter. |
sessionID | string | The user’s session ID. This should be the same value for the same session/user combination. Unlike v2, the server does not generate a session ID when this is missing. |
isTestMode | string | Send "true" to exclude the event from production queries, otherwise "false". Always send this value: the server adds no default in v3, and every dashboard query filters on it, so an event without it does not show up in the dashboard. |
floatValue | number | A numeric measurement. Use for any numeric operations, such as building averages or sums. |
| any other key | any JSON value | Your own parameters. See below. |
Custom Parameters
Add your own parameters as further top-level keys of the event. Keys should be in the format Scope.OptionalSubScope.key, with the scopes capitalized and the key starting with a lowercase letter. This is not enforced but helps you organize your dimensions and fit in better with TelemetryDeck’s internal keys.
Values keep their JSON type. Strings, numbers, and booleans are stored as sent. The event must not contain nested objects. The behavior of arrays is left undefined and should be avoided (except if you have a deep knowledge of multi-value dimensions in Apache Druid).
The server does not rename or clean your keys in v3. Use only letters, digits, dots, and underscores in key names.
Properties Added by the Server
The server changes or adds these properties before it stores the event. Everything else passes through untouched.
| Property | Value |
|---|---|
clientUser | Replaced by the salted SHA256 hash of the value you sent. |
TelemetryDeck.API.namespace | The namespace from the URL. |
TelemetryDeck.API.Ingest.version | events.v3 |
Unlike v2, the server does not add the legacy payload list or the telemetryAPIVersion property.
Responses
| Status | Body | Meaning |
|---|---|---|
| 200 | The number of accepted events, for example 2 | All events were accepted. An empty array returns 0. |
| 422 | A JSON error message | The body is not a JSON array, or at least one event is invalid. The server checks the whole batch before it stores anything, so a single invalid event rejects the whole request and nothing is stored. |
| 503 | queue is full, retry later | The server cannot accept events right now. The response carries a Retry-After: 5 header. Wait 5 seconds and send the batch again. |
An event is invalid if:
appID,clientUser, orreceivedAtis missing or not a string,appIDis empty and the namespace in the URL is blank,receivedAtis not a valid ISO 8601 date/time.
Web Events Endpoint
Send website events as a POST request to https://nom.telemetrydeck.com/v3/w/. This is the endpoint the Web SDK 3.0 uses. The server derives browser, system, location, and campaign data from the request, so the client only needs to send the URL.
The endpoint takes exactly one event per request. The body is a single JSON object, not an array.
The server parses the body as JSON no matter which Content-Type header the request carries. The Web SDK uses this to send its last event with navigator.sendBeacon() as text/plain, which needs no CORS preflight and therefore survives the page unload. The endpoint answers CORS preflight requests for any origin.
Here’s an example in cURL:
curl -X "POST" "https://nom.telemetrydeck.com/v3/w/" \
-H 'Content-Type: application/json; charset=utf-8' \
-H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' \
-d $'{
"appID": "AAAA-BBBBBBBB-CCCC-DDDD",
"url": "https://example.com/blog/post?utm_source=newsletter",
"referrer": "https://news.example.org/",
"locale": "en-US",
"isTestMode": "false"
}'Body Structure
Each web event must have these properties:
| Property | Type | Description |
|---|---|---|
url | string | The full URL of the page, including the query string. Must not be empty. The server splits the URL into its parts and stores it without the query string and fragment. |
appID or namespace | string | Your app’s ID, or your organization’s namespace. At least one of the two must be a non-empty string. You can send both. The server uppercases appID. It stores namespace as TelemetryDeck.API.namespace and removes the namespace key. |
These properties are optional:
| Property | Type | Description |
|---|---|---|
type | string | The type of event. Defaults to pageview. |
receivedAt | string | The date and time at which the event happened, as an ISO 8601 string. Defaults to the time the server received the event. |
isTestMode | boolean or string | If true or "true", the event is excluded from production queries. The server stores the value as the string "true" or "false". Defaults to "false". |
referrer | string | The URL of the page that referred the visitor. Used to build TelemetryDeck.Navigation.identifier. |
locale | string | The visitor’s locale, for example en-US. |
| any other key | any JSON value | Your own parameters, stored as sent. See Custom Parameters above for the naming rules. |
Do not send clientUser or sessionID. The server replaces both with its own visitor identifier, see below.
Properties Derived by the Server
The server adds these properties to every web event. If your event already contains one of these keys, the server value wins.
From the User-Agent header:
platform,systemVersion,majorSystemVersion,majorMinorSystemVersionbrowser,browserVersion,device,modelNameisMobile,isTablet,isTouchCapable,isDesktop,isBot(booleans)
From the url:
urlwithout query string and fragment,host,path,schemecombinedSource, plus each ofref,source,src,utm_source,utm_medium,utm_campaign,utm_term,utm_content,MSCLKID, andGCLIDthat appears in the query string. All other query parameters are dropped.
From the visitor’s IP address:
country.isoCode,country.isInEuropeanUnion,continent.code
The server reads the IP address from the leftmost entry of the X-Forwarded-For header, or from the connection when that header is missing. The IP address itself is never stored.
Other properties:
TelemetryDeck.API.Ingest.versionis set toweb.v3.TelemetryDeck.Navigation.identifieris set toreferrer -> url, with the cleanedurl.
See Default Parameters for a description of each property.
Visitor Identifiers
Web events use no cookies and no fingerprinting. The server builds clientUser from the app ID (or the namespace when there is no app ID), the visitor’s IP address, the User-Agent string, and a salt that changes every day. It hashes this with SHA256, then hashes the result again with a secret salt. The same visitor of the same site gets the same identifier within a day, and a new one the next day.
sessionID is set to the same value as clientUser.
This is the same construction that the v2 web endpoint uses, so user counts stay continuous when a site moves from an older Web SDK to version 3.0.
Responses
| Status | Body | Meaning |
|---|---|---|
| 200 | OK | The event was accepted. |
| 422 | A JSON error message | The body is not a JSON object, url is missing or empty, neither appID nor namespace is set, or receivedAt is not a valid ISO 8601 date/time. |
| 503 | queue is full, retry later | The server cannot accept events right now. The response carries a Retry-After: 5 header. |
Events Sent by the Web SDK
Web SDK 3.0 sends two event types per page load to the web events endpoint. You can send the same events from your own code.
Every event from the Web SDK contains appID, url, referrer, locale, isTestMode (as the string "true" or "false"), TelemetryDeck.SDK.name (WebSDK), TelemetryDeck.SDK.version, and TelemetryDeck.SDK.nameAndVersion.
pageview
Sent when the page loads. The Web SDK sends no type, so the server sets it to pageview.
TelemetryDeck.Web.pageLeave
Sent once per page load, the first time the page is hidden or unloaded: tab switch, tab close, navigation away, or app switch on mobile. It does not fire again if the visitor returns to the tab. It carries these engagement parameters in addition to the base parameters:
| Property | Type | Description |
|---|---|---|
TelemetryDeck.PageEngagement.scrollDepth | number | The deepest point of the page that has been in the viewport, as a percentage of the page height from 0 to 100. A page that fits in the viewport reports 100. |
TelemetryDeck.PageEngagement.scrollDepthMilestone | string | The scroll depth rounded down to one of "0", "25", "50", "75", or "100". Use this for grouping. |
TelemetryDeck.PageEngagement.engagedSeconds | number | Whole seconds the page was visible in the foreground. Time in a background tab is not counted. |
Site owners can turn these events off with data-page-engagement="false", see the Web SDK guide.
Differences from v2
| Topic | v2 | v3 |
|---|---|---|
| Custom parameters | Inside the payload object | At the top level of the event |
| Value types | Converted to strings, except numbers inside payload | Stored as sent |
| Namespace | Optional: URL path or TelemetryDeck.API.namespace in the payload | Events: required in the URL path. Web: optional namespace property |
receivedAt | Optional, moved into the last 24 hours | Events: required, stored as sent. Web: optional, defaults to now |
sessionID | Generated once a day when missing | Events: stored as sent, never generated. Web: always set by the server |
isTestMode | Defaults to "false" | Events: no default, always send it. Web: defaults to "false" |
| Web request body | One object with a payload | One flat object |
Legacy payload list and telemetryAPIVersion | Added by the server | Not added |
TelemetryDeck.API.Ingest.version | v2 for events, v2web for web events | events.v3 for events, web.v3 for web events |
| Response body on success | OK | Events: the number of accepted events. Web: OK |
Reserved Keys
Some keys are reserved for internal use by TelemetryDeck. See the complete list of reserved parameters which you’re not allowed to include in your events.
In general, avoid using parameter names that begin with the TelemetryDeck. scope, as this is reserved for parameters set by TelemetryDeck SDKs and the ingestion server.