Quick start — first matched ad
From install to a matched ad on screen in six steps.
Use this guide when you already have a React or Next.js app and want Adzen to deliver a contextual ad alongside some content — an assistant message, an article, a search result, anything with text to match against.
This is the generic path: no CopilotKit, no AG-UI. If you’re on CopilotKit/AG-UI agents, see the CopilotKit quick start.
Prerequisites
- React
>=18 - A place to run server code with access to your API key (a Next.js route handler / server action, or any Node
>=18backend) - An Adzen API key
Step 1: Install
npm install @adzenai/ai @adzenai/coreOr with pnpm:
pnpm add @adzenai/ai @adzenai/coreStep 2: Configure your environment
Put your key somewhere only the server can read it:
ADZEN_API_KEY=your_api_keyStep 3: Fetch a placement on the server
fetchAdzenAd sends your key as the X-API-Key header, so it must run server-side. It returns an AdzenPlacement, or null when there is no match (it never throws).
// 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, // the text to match an ad against
messageId: crypto.randomUUID(), // any stable id for this placement
location: process.env.ADZEN_LOCATION, // optional geo-targeting
});
return Response.json({ placement });
}You can also call fetchAdzenAd directly inside a server action or a React Server Component — anywhere that runs on the server.
Keep the key server-side. Calling fetchAdzenAd from a browser (client
component) would ship your API key in the bundle. Always fetch on the server
and pass only the returned AdzenPlacement to the client.
Step 4: Render the ad
<AdzenCard> is a client component ("use client"). Pass it the placement your server returned. It renders null when ad is null, so it’s safe to render unconditionally.
"use client";
import { useEffect, useState } from "react";
import { AdzenCard } from "@adzenai/ai/react";
import type { AdzenPlacement } from "@adzenai/ai";
export function SponsoredSlot({ content }: { content: string }) {
const [ad, setAd] = useState<AdzenPlacement | null>(null);
useEffect(() => {
fetch("/api/ad", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ content }),
})
.then((r) => r.json())
.then((d) => setAd(d.placement))
.catch(() => {});
}, [content]);
// AdzenCard's `ad` prop doesn't accept null, so render it only once there's a placement.
return ad ? <AdzenCard ad={ad} /> : null;
}AdzenCard fires the render and view impression beacons for you — no extra wiring needed.
Step 5 (optional): Render your own markup
Your product, your design language. To use your own layout while keeping impression tracking, use useAdImpressions. It returns a ref to attach to your ad element; the render beacon fires on mount and the view beacon fires once the element has been visible long enough.
"use client";
import { useAdImpressions } from "@adzenai/ai/react";
import type { AdzenPlacement } from "@adzenai/ai";
export function CustomAd({ ad }: { ad: AdzenPlacement | null }) {
const { ref } = useAdImpressions(ad);
if (!ad) return null;
return (
<aside ref={ref} className="my-ad">
<span className="label">Sponsored · {ad.advertiser_name}</span>
<a href={ad.destination_url} target="_blank" rel="noopener sponsored">
{ad.headline} — {ad.cta_text}
</a>
</aside>
);
}Step 6: Verify
- Trigger a fetch (send a message, load an article, etc.).
- Confirm the placement appears when Adzen returns a match, and nothing renders when it doesn’t.
- In the Network tab, confirm a
GETto the render impression URL fires when the ad mounts. - Keep the ad on screen for ~1 second (default viewability threshold) and confirm the view impression
GETfires. - Your Adzen contact can confirm the impression events were recorded.
Next
- Configuration — fetch options,
AdzenCardprops,useAdImpressions, impression beacons - Integration walkthrough — the full end-to-end guide
- How it works — architecture and API reference