Endpoints
Contextual Ads
Get ads matched to conversation context.
POST
The primary ad endpoint. Send conversation messages and get back a contextually matched ad with generated creative.
Null fields are omitted from the response.
Experiment identity is already baked into
Fix the request by forwarding the end user’s real IP and User-Agent — see Request ads → Forwarding device server-side.
Headers
Your Gravity API key. Format:
Bearer <key>Body
Conversation history. Array of
{role, content} message objects. The engine uses the last few messages for contextual matching.Session identifier. Used for frequency capping, experiment bucketing, and reporting. If the Gravity pixel is installed and you’re on
@gravity-ai/api ≥ 1.1.7, the SDK auto-forwards the pixel’s gr_sess_-prefixed session ID (30-min idle timeout, 1-day max) via window.gravityPixel.getSessionId(). Pass your own sessionId to override if you have a better session scope.Ad placement configuration. 1–10 placements per request.
| Field | Type | Description |
|---|---|---|
placement | string | Placement type. One of above_response, below_response, inline_response, left_response, right_response, search_result, top_page, bottom_page, center_page, left_page, right_page. |
placement_id | string | Unique slot identifier on your side. Ties dashboard analytics back to the spot in your UI. |
Optional. User context for targeting and attribution. All fields are optional on the wire — the engine accepts any shape and passes extras through.
Extra fields (gender, age, subscription tier, interests, etc.) are accepted and stored as request context.
| Field | Type | Description |
|---|---|---|
id | string | Stable per-user identifier on your side. Used for frequency capping, attribution, and identity linking. The SDK defaults this to "anonymous" if you don’t supply one. |
ip | string | User’s IP address. Auto-populated by the SDK from the incoming request. |
email_hash | string | SHA-256 of email.strip().lower(). Canonical email field (matches the email_hash used by the Index /search API). Passing this significantly improves attribution; see Data quality. The legacy hashed_email alias and a raw email field are still accepted for backward compatibility. |
hashed_phone | string | SHA-256 of digits-only phone. |
End-user device signals.
Extra fields (
ip and ua are required — requests missing either are rejected with HTTP 400 (see Errors). They power fraud/bot detection plus the Device and Geography breakdowns in your dashboard. The browser SDK auto-populates them via gravityContext(); server-side callers must forward the client-collected device (the request reaching Gravity otherwise carries your server’s UA/IP, not the end user’s).| Field | Type | Required | Description |
|---|---|---|---|
ua | string | Yes | End-user User-Agent. Drives fraud detection and the mobile/tablet/desktop split. Send the end user’s real browser/app UA — not your server’s. |
ip | string | Yes | End-user IP. Drives fraud detection and geo-targeting. Auto-filled by the SDK from the incoming request; set it explicitly for direct HTTP. |
country | string | No | 2-letter ISO country code. Optional override — also derived from ip. |
os | string | No | Operating system (e.g. iOS, Android, Windows). |
ifa | string | No | Mobile advertising ID (IDFA / GAID), for app publishers. |
id | string | No | Your stable per-device identifier. |
timezone, locale, browser, device_model, …) are accepted and stored. Native-app/non-browser clients should send their real client UA (CFNetwork/Darwin, okhttp, ktor-client, …) plus the end user’s IP. See Request ads → Device signals.Optional. Minimum relevancy threshold, 0.0–1.0. When omitted, the engine falls back to the publisher baseline configured in your dashboard. Both SDKs default to
0.2.Optional. Array of topic strings to exclude from matching (e.g.
["politics"]).Optional. When
true, returns test creative and skips billing/metrics. The SDKs set this from the inverse of their production flag.Response
On a successful match the endpoint returns HTTP200 with a JSON array of ad objects — one per requested placement:
The SDKs wrap this array in a convenience object —
{ ads, status, elapsed } in JS, AdResult(ads, status, elapsed_ms, ...) in Python — but that envelope is SDK-only and does not appear on the wire.Ad object
| Field | Type | Description |
|---|---|---|
adText | string | Generated ad copy, contextually matched |
title | string | Product/campaign title |
brandName | string | Advertiser brand name |
cta | string | Call to action text |
url | string | Landing page URL |
favicon | string | Brand favicon URL |
clickUrl | string | Tracked click URL — use this for links |
impUrl | string | Impression pixel URL — fire when ad is visible |
placement | string | Echoes back the placement type this ad filled |
placement_id | string | Echoes back your slot correlation ID |
campaignId | string | Campaign identifier for the matched ad |
leadForm | object | Lead form configuration, when the campaign has a lead form attached |
renderer_spec | object | Server-delivered ad-unit layout — see Server-rendered ads |
renderer_spec
renderer_spec is attached per placement, configured on Gravity’s side — there is no request parameter to opt in or out. When your placement has a default design set up, every ad served on it carries the spec; when it doesn’t, the field is absent and you render your own look (or the SDK’s built-in one). To get a design configured for your placement, email support@trygravity.ai with your placement_id. The spec format and rendering rules are documented in Server-rendered ads.
When an experiment is active, the ad object also includes:
| Field | Type | Description |
|---|---|---|
variant | string | Human-readable arm label |
experiment_id | string | Canonical experiment ID |
composition_id | string | Per-render composition ID (unique per impression) |
renderer_key | string | Which renderer to use |
composition | object | Tokens and props for the assigned renderer |
impUrl and clickUrl — downstream analytics join automatically.
No ad available
When no ad matches the context (or the request is filtered as a bot, times out, or hits an unrecoverable error), the endpoint returns an HTTP204 No Content with an empty body — there is no JSON payload. Your UI should gracefully hide the slot in that case.
Errors
Requests missing required fields are rejected with HTTP400 and a JSON body naming each missing field. device.ip and device.ua are validated on every ad request (blank/whitespace-only values count as missing):

