@cherry_ai/api
v0.2.0
Published
Cherry JS SDK for context collection and ad fetching
Readme
@cherry_ai/api
Publisher SDK for Cherry ads.
CherryContext.collect() gathers device signals. Cherry.getAds() reads cherryContext from your server request and POSTs to Cherry.
Install
pnpm add @cherry_ai/apinpm install @cherry_ai/apiClient — chat UI
import { CherryContext } from "@cherry_ai/api";
const cherryContext = new CherryContext().collect({
sessionId: chatSession.id,
user: { userId: currentUser.id },
});
fetch("/api/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ messages, cherryContext }),
});sessionId (UUID) and user.userId are required. Device fields (ua, timezone, locale) are collected automatically in the browser. You can pass them explicitly to override.
Server — fetch ads
import { Cherry, CherryError } from "@cherry_ai/api";
const cherry = new Cherry({ apiKey: process.env.CHERRY_API_KEY! });
app.post("/api/chat", async (req, res) => {
const { messages } = req.body;
const adPromise = cherry
.getAds(req, messages, [
{ placement: "below_response", placementId: "main" },
])
.catch((error) => {
if (error instanceof CherryError) {
return { ads: [] };
}
throw error;
});
// stream your LLM response...
const { ads } = await adPromise;
res.write(`data: ${JSON.stringify({ type: "done", ads })}\n\n`);
res.end();
});getAds throws CherryError with statusCode. Wrap with try/catch or Promise.allSettled so chat is not blocked.
| Case | statusCode |
|------|--------------|
| Missing apiKey | 401 |
| Missing cherryContext | 400 |
| Cherry API 400 / 401 / 5xx | body statusCode or HTTP status |
| Timeout | 408 |
| Network | 503 |
Next.js route handler
import { Cherry } from "@cherry_ai/api";
const cherry = new Cherry({ apiKey: process.env.CHERRY_API_KEY! });
export async function POST(request: Request) {
const body = await request.json();
try {
const { ads } = await cherry.getAds(
{ body, headers: Object.fromEntries(request.headers) },
body.messages,
[{ placement: "below_response", placementId: "main" }],
);
return Response.json({ ads });
} catch {
return Response.json({ ads: [] });
}
}new Cherry(opts)
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| apiKey | string | required for getAds | Cherry API key (chk_...) |
| baseUrl | string | http://api-dev.cherry.scribbledao.com | Cherry API origin |
| timeoutMs | number | 3000 | Request timeout |
| relevancy | number | 0.5 | Min relevancy (0–1) |
| excludedTopics | string[] | [] | Topics to skip |
Point baseUrl at a local api.cherry with { baseUrl: "http://localhost:3010" }.
cherry.getAds(req, messages, placements, overrides?)
Reads cherryContext from req.body, forwards the end-user IP as x-forwarded-for (x-forwarded-for → x-real-ip → socket), and POSTs camelCase JSON to {baseUrl}/ads with x-cherry-api-key.
overrides is a partial TCherryOptions (apiKey, baseUrl, timeoutMs, relevancy, excludedTopics) applied for that call only.
Placements: above_response | below_response | loader_text | loader_div | left_response | right_response.
Empty fill is { ads: [] } (HTTP 200). That is success, not an error.
Data flow
┌──────────────────┐ cherryContext in body ┌──────────────────┐ POST /ads ┌────────────┐
│ Client code │ ─────────────────────────▶ │ Your server │ ───────────▶ │ Cherry API │
│ CherryContext │ │ cherry.getAds() │ │ │
└──────────────────┘ └──────────────────┘ └────────────┘Development
Run from this repo (pkg.cherry):
pnpm install
pnpm check
pnpm test
pnpm buildReleasing
Changesets live in this repo only. Until 1.0.0: patch = fix, minor = feature or breaking, major = 1.0.0.
pnpm changeset # add a changeset on a feature branch
pnpm changeset:status # preview the bumpOn merge to main, CI opens a chore: update version PR (pnpm changeset:version). Merging that PR publishes to npm (pnpm changeset:publish), tags vX.Y.Z, and creates a GitHub Release. Set the GitHub Actions secret NPM_TOKEN.
