> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trygravity.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Integration guide

> The full AI platform lifecycle — integrate, test, pixel, ship, monitor, get paid.

This is the long version of the [Quickstart](/ai-platforms/quickstart). If you just want the 5-minute happy path, start there. This guide covers everything around the edges: the full lifecycle from first line of code to recurring payouts.

## Lifecycle overview

<Steps>
  <Step title="Design">
    Decide where ads go in your UI, which placements you need, and whether you're using the SDK or calling the API directly.
  </Step>

  <Step title="Integrate">
    Install the SDK. Wire `gravityContext()` on the client, `gravity.getAds()` on the server, `GravityAd` (or your own renderer) in the UI.
  </Step>

  <Step title="Test">
    Keep `production: false`. Verify placements render, impressions fire on visibility, clicks route through `clickUrl`.
  </Step>

  <Step title="Install the pixel">
    Drop the [Gravity pixel](/ai-platforms/pixel) onto your site. One `<script>` tag — mints a visitor ID the SDK automatically forwards on every ad request, which tightens targeting and attribution. If you also open ad links inside a native in-app browser, add the [in-app browser install](/ai-platforms/pixel#in-app-browser-webview) on top.
  </Step>

  <Step title="Go live">
    Flip `production: true`. Watch the [going-live checklist](/ai-platforms/going-live).
  </Step>

  <Step title="Optimize">
    Let [experiments](/ai-platforms/experiments) test ad variants against your real traffic. The engine moves traffic toward the highest-converting options automatically.
  </Step>

  <Step title="Get paid">
    Monitor [payouts](/ai-platforms/payouts) in the dashboard. Revenue accrues per impression.
  </Step>
</Steps>

## Pick placements

A **placement** is where an ad sits relative to your UI. Every ad request declares one or more. Common options:

| Placement                          | When to use                                             |
| ---------------------------------- | ------------------------------------------------------- |
| `below_response`                   | Under the assistant's message. Most common in chat UIs. |
| `above_response`                   | Pinned header above the response. Use sparingly.        |
| `inline_response`                  | Inside the response stream (markdown).                  |
| `left_response` / `right_response` | Sidebar next to the response.                           |
| `search_result`                    | Within a search result list.                            |
| `top_page` / `bottom_page`         | Static, page-level placement.                           |

Each placement has a `placement_id` you pick — anything identifying the slot (`"main"`, `"mobile-footer"`, etc.). Keep them stable across requests; you'll see per-slot metrics in the dashboard.

You can request up to **10 placements per call** and the engine returns an ad per slot (or `null` per slot with no fill).

## Choose: SDK or API

Both hit the same engine. The SDK is a thin wrapper that handles context preparation, impression tracking, retry, and experiments. The API is what the SDK calls under the hood.

**Use the SDK when:**

* You're on Node, TypeScript, Python, or React
* You want pre-built visual variants
* You want to participate in experiments (which auto-optimize your creative mix)
* You want impression tracking to "just work"

**Use the API directly when:**

* You're on an unusual language / runtime
* You render ads in a heavily custom way that can't use `GravityAd`
* You're prototyping or building a CLI / non-UI surface

You're not locked in either way. The API is fully supported and never going away.

## Typical integration shapes

### Next.js / App Router

```ts theme={null}
// app/api/chat/route.ts
import { Gravity } from '@gravity-ai/api';

const gravity = new Gravity({ production: process.env.NODE_ENV === 'production' });

export async function POST(req: Request) {
  const { messages } = await req.json();

  const adPromise = gravity.getAds(req, messages, [
    { placement: 'below_response', placement_id: 'main' }
  ]);

  const stream = new ReadableStream({
    async start(controller) {
      for await (const chunk of callLLM(messages)) {
        controller.enqueue(`data: ${JSON.stringify({ type: 'chunk', content: chunk })}\n\n`);
      }
      const { ads } = await adPromise;
      controller.enqueue(`data: ${JSON.stringify({ type: 'done', ads })}\n\n`);
      controller.close();
    },
  });

  return new Response(stream, { headers: { 'Content-Type': 'text/event-stream' } });
}
```

### Express

```ts theme={null}
import { Gravity } from '@gravity-ai/api';
const gravity = new Gravity({ production: true });

app.post('/api/chat', async (req, res) => {
  const { messages } = req.body;
  const adPromise = gravity.getAds(req, messages, [{ placement: 'below_response', placement_id: 'main' }]);
  // ... stream LLM, await adPromise, append ads
});
```

### FastAPI / Python

```python theme={null}
import asyncio
from gravity_sdk import Gravity

gravity = Gravity(production=True)

@app.post("/api/chat")
async def chat(request: Request):
    body = await request.json()
    ad_task = asyncio.create_task(
        gravity.get_ads(request, body["messages"], [
            {"placement": "below_response", "placement_id": "main"}
        ])
    )
    # ... stream LLM, await ad_task, append ads
```

## Error handling

The SDK never throws. Failure modes:

| Situation                 | Result                                           |
| ------------------------- | ------------------------------------------------ |
| Network error / timeout   | `{ ads: [] }` (JS) / `AdResult(ads=[])` (Python) |
| 204 No Content (no match) | `{ ads: [] }`                                    |
| Invalid API key           | `{ ads: [] }` + warn in logs                     |
| Downstream engine error   | `{ ads: [] }` + warn                             |

Your UI should always be safe to render with an empty ads array. Never block your LLM response on the ad fetch.

## Going live checklist

See [Going live](/ai-platforms/going-live). Short version: flip `production: true`, verify impressions land, confirm clicks route through `clickUrl`, watch the dashboard.

## What's next

<CardGroup cols={2}>
  <Card title="Request ads" icon="arrow-right-arrow-left" href="/ai-platforms/request-ads">
    Server-side patterns in depth.
  </Card>

  <Card title="Show ads" icon="browser" href="/ai-platforms/show-ads">
    Client rendering, variants, custom renderers.
  </Card>

  <Card title="Pixel" icon="code" href="/ai-platforms/pixel">
    Drop `gr-pix.js` on your site — SDK auto-picks up the visitor ID for better attribution. Optional WebView install for native apps.
  </Card>

  <Card title="Experiments" icon="flask" href="/ai-platforms/experiments">
    How the engine auto-optimizes creative mix for your traffic.
  </Card>
</CardGroup>
