Overview
One call on your server, one component in your UI. Adzen does the matching; you keep the experience.
The @adzenai/ai SDK is the fastest way to put Adzen into an assistant built on
plain React or Next.js. You pass it the message a user sent. It returns the
single ad that genuinely fits that turn — or nothing, when nothing does — and
tracks the impression once your user can actually see it.
No CopilotKit or AG-UI required. If you are building on CopilotKit agents, the CopilotKit integration is the faster path.
The matching is ours, the surface is yours. Adzen reads the turn the way a person would — see We understand the turn. Where the ad sits, how it looks and when it appears stay entirely in your product.
How it works
The flow is two steps:
- Fetch a placement on the server with
fetchAdzenAd(...). Your API key stays server-side. - Render the returned placement on the client — either with the built-in
<AdzenCard>or your own markup viauseAdImpressions.
Where each piece runs
| Layer | Role |
|---|---|
| Your UI (React/Next) | renders content and the ad placement |
| Your server | calls the Adzen /process API with your key, returns the placement to the client |
| Adzen | matches an ad to the content and provides impression beacons |
┌────────────────────────────────────────────┐
│ Your React / Next.js UI │
│ ┌───────────────────────────────────────┐ │
│ │ Content (message, article, …) │ │
│ └───────────────────────────────────────┘ │
│ ┌───────────────────────────────────────┐ │
│ │ <AdzenCard ad={placement} /> │ │
│ └───────────────────────────────────────┘ │
└───────────────▲────────────────────────────┘
│ AdzenPlacement (JSON)
┌───────────────┴───────────────────────────────┐
│ Your server (route handler / server action) │
│ fetchAdzenAd({ apiKey, content, messageId }) │
└───────────────▲───────────────────────────────┘
│ POST /process (X-API-Key)
┌───────────────┴───────────────────────────────┐
│ Adzen API │
└───────────────────────────────────────────────┘Adzen can never break your product. fetchAdzenAd returns null (never
throws) on no-match, timeout, or error, and <AdzenCard> renders nothing when
there is no ad — so your UI is unaffected when there’s no placement.
An empty response is a feature. It means nothing cleared our relevance and brand-safety bar for that turn, and we declined to fill the slot rather than show your users something that does not belong there.
Before you start
- Get an Adzen API key from your Adzen account representative.
- Have a React
>=18app with somewhere to run server code (a Next.js route handler / server action, or any Node>=18backend).
What the code looks like
Server — fetch a placement (key stays server-side):
// app/api/ad/route.ts (Next.js App Router)
import { fetchAdzenAd } from "@adzenai/ai";
export async function POST(req: Request) {
const { content } = await req.json();
const placement = await fetchAdzenAd({
apiKey: process.env.ADZEN_API_KEY!,
content,
messageId: crypto.randomUUID(),
});
return Response.json({ placement });
}Client — render the placement:
"use client";
import { AdzenCard } from "@adzenai/ai/react";
// `placement` is what your route returned (null when there's no match).
// AdzenCard's `ad` prop doesn't accept null, so render it only when there is one.
{placement && <AdzenCard ad={placement} />}Choose your path
What your users see
- Your content renders normally with zero dependency on the ad request.
- A sponsored placement appears where you render
<AdzenCard>(or your own markup) when Adzen returns a match. - A render impression fires when the ad mounts; a view impression fires once it has been visible for the viewability threshold.
- Click-through links open the advertiser destination.
Next
- Quick start — your first matched ad.
- Integration walkthrough — custom rendering and verification.
- Configuration — tune fetching, viewability, and impressions.