The server resolves the user’s location and device from request headers. These are optional but strongly recommended for server-side integrations — sending them skips the server-side IP lookup and reduces latency.
User’s device type: "mobile" or "desktop". Required when X-User-Country is present. This is critical for device-targeted campaigns — without it, targeting will not work correctly.
City name (e.g. "San Francisco"). Optional but recommended for city-level targeting.
If you send X-User-Country without X-User-Device and X-User-Timezone, the request will be rejected with a 400 Bad Request. These three headers must always be sent together.
If none of the above headers are sent, the server falls back to IP-based geolocation using X-Forwarded-For. For direct calls (browser or mobile app → our API), this is handled automatically by the load balancer. If you’re proxying requests through your own backend, you must forward the user’s IP via X-Forwarded-For — otherwise the server sees your server’s IP and geo will be wrong.
If unsure about which headers to send, contact marco@thrads.ai and we’ll help you set it up.
Unique, anonymous user identifier (e.g. a UUID like user_a1b2c3d4-...). Must be stable across sessions for the same user. Do not use email, name, or other PII. On web, store in localStorage; on mobile, use the platform’s persistent storage (e.g. UserDefaults on iOS, SharedPreferences on Android).
contextual only Conversation identifier. One unique ID per conversation, not per user. Reset when user starts a new chat. Required when request_type is "contextual".
contextual only Conversation history. Required when request_type is "contextual". Each message must have a role ("user" or "assistant") and content. PII (names, emails, addresses, etc.) should be masked or removed before sending.
ISO 8601 (e.g. "2025-11-23T10:00:00Z") or Unix timestamp (e.g. 1700730000). Used for conversation continuity tracking — without timestamps, the server cannot merge partial message histories and conversation analytics will be degraded.
Publisher configuration for pacing, creative constraints, and analytics. All fields are optional — omitted fields fall back to defaults set on your chatbot in the platform dashboard.
contextual only Minimum number of requests between consecutive ads. For example, 3 means an ad can appear at most once every 3 requests. null uses your dashboard setting. 0 disables the frequency check.
Maximum character length for ad headlines. Minimum value is 30. When set, the system guarantees no headline exceeds this limit — creatives that don’t fit are filtered or replaced.
Set to true if your UI only makes specific elements clickable — logo, advertiser name, CTA button — rather than the entire ad surface. Analytics-only signal: it does not change the response payload. Lets us segment click-through performance by click-zone style. Defaults to false (full ad surface clickable).
Ad formats the publisher can render. The server picks the best match from this list.
Contextual:["sponsored_message"] (default), ["sponsored_poll"], or ["sponsored_message", "sponsored_poll"].
Opener:["sponsored_message"], ["sponsored_prompt"], or both. Defaults to ["sponsored_prompt"] when omitted. Do not include sponsored_poll on opener requests.
When contextual requests include both sponsored_message and sponsored_poll, Thrad selects the campaign through the normal contextual auction, then uses campaign delivery settings to choose whether the returned creative is a message or poll.
contextual only Current turn number in the conversation. Used for ad offset pacing and analytics. Must be >= 0. Only needed if you’re not calling the endpoint on every user-assistant exchange — if you are, the system derives this from the message count.
contextual only Number of requests since the last ad was shown in this conversation. Used by the max frequency check. Optional — the system tracks this automatically. Only send this if you want to override the server-side counter.
contextual only When true, the system serves an ad as a last resort even if the conversation context doesn’t strongly match any campaign. The normal pipeline (contextual scoring → retargeting → exploration) runs first; force only kicks in when everything else returns no bid. Hard filters (banned content, geo targeting, budget caps) still apply — force never overrides safety or budget constraints.We recommend starting withoutforce (the default) and only enabling it if your fill rate is too low. See Maximising Fill Rate for guidance.
IAB standard age range. Accepted values: "0-12", "13-17", "18-24", "25-34", "35-44", "45-54", "55-64", "65+". When the value is "0-12" or "13-17", the request is treated as underage: the auction is skipped and the response is 200 OK with "bid": null. Send this whenever your platform knows the user’s age — it’s the only way to comply with age-gating on Thrad’s side.
IAB TCF v2 consent string generated by your CMP (e.g. OneTrust, Quantcast) when the user interacts with your cookie banner. Required for EU/UK users if you want oRTB DSPs to bid on that traffic — without it, EU-based DSPs will scrub or skip the request. Pass the raw base64-encoded string exactly as produced by your CMP. Non-EU publishers can omit this field.
IAB CCPA US Privacy string (e.g. "1YNN", "1---"). Generated by your CMP for California/US users. Forwarded to oRTB DSPs as regs.us_privacy. Non-US publishers can omit this field.
A 200 OK response with "bid": null is not an error - it means the auction ran successfully but no DSP submitted a winning bid. This is normal behavior.
The ad format returned (e.g. "sponsored_message", "sponsored_prompt", "sponsored_poll"). Matches one of the values from the request’s ad_formats array.
Sponsored message and prompt click tracking URL. Use this for ad clicks — it tracks the click and redirects to the advertiser’s landing page. Poll option clicks use vote_url, not url.
sponsored_message only. Render mode: "text" (compact, logo only) or "image" (full product photo). When missing, treat as "text". See Ad Formats & Rendering for the full creative-field rules per mode.
Product/hero photo URL. Present when placement="image", absent when placement="text". Setting config.image_enabled=false forces text mode, so image_url will never be returned.