Custom GPT Integration Guide
This guide explains how to integrate the In-Chat Recommendations API into a Custom GPT experience.
It covers:
- What a Custom GPT can and cannot do
- Recommended architecture (client/server)
- How to call the API with GPT Actions
- How to render ads and report impressions correctly
1) What You’re Building
You are integrating ads (recommendations) into a chat conversation:
- The user chats with your Custom GPT.
- Your Custom GPT triggers an API call to fetch ads that match the conversation context.
- Your UI renders the ads as cards/links.
- You fire the
reportServedimpression pixel for displayed ads.
2) Custom GPT Capabilities (Important)
A Custom GPT can:
✅ Call external APIs via Actions
✅ Format structured output (JSON) for your UI
✅ Decide when to request ads (based on conversation context)
A Custom GPT cannot:
❌ Execute navigator.sendBeacon() directly
❌ Fire browser pixels itself
❌ Reliably access a user’s browsing environment
✅ Therefore:
- The GPT should fetch ads
- Your frontend (or host app) should render ads and fire pixels
3) Recommended Architecture
Option A (Recommended): Custom GPT + Backend Proxy + Frontend UI
Custom GPT → Your backend proxy → In-Chat Recs API
│ │
└────────→ UI renders ads + fires reportServed pixel
Why this is best:
- Keeps
keysecure (not exposed client-side) - Allows logging, monitoring, retries
- Enables safer filtering of keywords/categories
Option B: Custom GPT calls API directly
Possible, but only recommended if:
- Key exposure is acceptable for your use case
- You cannot deploy a backend proxy
4) What You Send to the API
Endpoint
Method: POST
URL:
https://mv.outbrain.com/Multivac/api/in-chat-recs
Query Parameters
| Parameter | Required | Description |
|---|---|---|
key | ✅ | Your API key |
contentUrl | ✅ | URL-encoded host page (e.g., test.com) |
widgetJSId | ✅ | Provided by Teads (e.g., APP_12) |
cors | ✅ | Must be true |
testMode | ❌ | Use true in dev/testing |
JSON Body
{
"keywords": ["investing", "savings", "personal finance"],
"iabCategories": ["IAB13"]
}
5) Configure a Custom GPT Action (OpenAPI Spec)
In the Custom GPT builder:
- Open Actions
- Add a new Action using an OpenAPI schema
- Point it at your backend proxy (recommended)
Example Action Endpoint (Proxy)
POST https://yourdomain.com/api/teads/in-chat-recs
Minimal OpenAPI Example
openapi: 3.0.3
info:
title: In-Chat Recommendations API Proxy
version: 1.0.0
servers:
- url: https://yourdomain.com
paths:
/api/teads/in-chat-recs:
post:
operationId: fetchInChatRecs
summary: Fetch in-chat ad recommendations
parameters:
- name: contentUrl
in: query
required: true
schema:
type: string
description: URL-encoded host page (e.g., test.com)
- name: testMode
in: query
required: false
schema:
type: boolean
description: Enable dev test mode
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
keywords:
type: array
items:
type: string
iabCategories:
type: array
items:
type: string
responses:
"200":
description: Recommendations response
content:
application/json:
schema:
type: object
✅ Your backend proxy should inject:
keywidgetJSIdcors=true
So the GPT never sees your secrets.
6) Backend Proxy (Recommended)
Your backend receives:
contentUrlkeywordsiabCategories- optional
testMode
Then it calls:
https://mv.outbrain.com/Multivac/api/in-chat-recs?key=...&contentUrl=...&widgetJSId=APP_12&cors=true
✅ Benefits:
- API key stays hidden
- You can filter keywords to prevent sensitive data leakage
- You can standardize
contentUrlto the host domain only
7) Rendering Ads in Your UI
Your UI should render each item in documents[] as an Ad Card:
- Clickable link (
url) - Creative image (
thumbnail_url) - Title (
content) - Sponsor label (
source_display_name) - Optional domain (
target_domain) - Optional CTA button (
cta)
Example UI pseudo-structure:
[thumbnail]
Title: content
Sponsored by source_display_name (target_domain)
[CTA button]
8) Reporting Impressions (Required)
Once ads are rendered, report impressions using the tracking URL:
- Read
reportServed - Append positions of displayed ads:
&pos=X-Y - Fire the pixel as GET or
navigator.sendBeacon