Examples

Every example on this page is self-contained: it includes imports, environment setup, and error handling so you can copy a single block and run it. Set these first:

export JOY_TOKEN_API_KEY="sk-xxx"
export JOY_TOKEN_OPENAI_BASE_URL="https://api.joytokens.ai/openai/v1"
export JOY_TOKEN_ANTHROPIC_BASE_URL="https://api.joytokens.ai/anthropic/v1"

All requests use model: "auto" �?there is no model environment variable. Base URLs come from Environments.

Chat Completions

import { JoyTokenClient } from "@joytoken/client-sdk-ts";
const apiKey = process.env.JOY_TOKEN_API_KEY;
if (!apiKey) throw new Error("JOY_TOKEN_API_KEY is required");
const joytoken = new JoyTokenClient({
apiKey,
openAIBaseUrl: process.env.JOY_TOKEN_OPENAI_BASE_URL,
});
const completion = await joytoken.chat.completions.create({
model: "auto",
messages: [{ role: "user", content: "Say hello in one sentence." }],
});
console.log(completion.choices[0]?.message?.content);

Responses

import { JoyTokenClient } from "@joytoken/client-sdk-ts";
const apiKey = process.env.JOY_TOKEN_API_KEY;
if (!apiKey) throw new Error("JOY_TOKEN_API_KEY is required");
const joytoken = new JoyTokenClient({
apiKey,
openAIBaseUrl: process.env.JOY_TOKEN_OPENAI_BASE_URL,
});
const response = await joytoken.responses.create({
model: "auto",
input: "Say hello in one sentence.",
});
console.log(response.output?.[0]?.content?.[0]?.text);

Anthropic Messages

import { JoyTokenClient } from "@joytoken/client-sdk-ts";
const apiKey = process.env.JOY_TOKEN_API_KEY;
if (!apiKey) throw new Error("JOY_TOKEN_API_KEY is required");
const joytoken = new JoyTokenClient({
apiKey,
anthropicBaseUrl: process.env.JOY_TOKEN_ANTHROPIC_BASE_URL,
});
const message = await joytoken.messages.create({
model: "auto",
max_tokens: 1024,
messages: [{ role: "user", content: "Say hello in one sentence." }],
});
console.log(message.content[0]?.text);

Streaming

Streaming works for Chat Completions, Responses, and Anthropic Messages. The examples below stream Chat Completions.

for await (const chunk of joytoken.chat.completions.stream({
model: "auto",
messages: [{ role: "user", content: "Count from 1 to 5." }],
})) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}

Orchestration

A model: "auto" request may be routed into orchestration (multi-task planning) mode. The gateway splits the request into subtasks, runs them, and aggregates the result. The SDK returns the raw gateway shape, so you branch on it:

  • choices[0].message.content is a JSON-encoded array of per-task outputs ({ "title", "content" }), not a plain string �?parse it before rendering. The top-level plan field confirms orchestration mode.
  • metadata is an array with one entry per task, including the reserved __planner__ (task_seq: 0) and __final__ (task_seq: 4) entries. Sum billing.credits_used across entries for the total cost.
  • When streaming, a planning event arrives first (a chunk whose orchestration.phase is planning, carrying the ordered plan), followed by the __planner__ metadata event. Then each task’s full content arrives as a chunk that carries an orchestration field (task_id, task_seq, task_status, title), each followed by one standalone metadata event (a single object, not an array), before [DONE].

See Routing for the full field reference.

type TaskOutput = { title: string; content: string };
const completion = await joytoken.chat.completions.create({
model: "auto",
messages: [{ role: "user", content: "Plan a one-day indoor trip in Shenzhen." }],
});
const raw = completion.choices[0]?.message?.content ?? "[]";
const metadata = (completion as any).metadata ?? [];
// Orchestration mode: content is a JSON array of task outputs.
if ((completion as any).plan && Array.isArray((completion as any).plan)) {
const tasks: TaskOutput[] = JSON.parse(raw);
for (const task of tasks) {
console.log(`## ${task.title}\n${task.content}\n`);
}
const totalCredits = metadata.reduce(
(sum: number, m: any) => sum + (m.billing?.credits_used ?? 0),
0,
);
console.log(`Total credits: ${totalCredits}`);
} else {
console.log(raw); // plain single-model response
}

Model list

const models = await joytoken.models.list({ locale: "en" });
for (const model of models.data.models) {
console.log(model.modelId);
}

Use { locale: "zh" } for Chinese descriptions; omitting locale defaults to English. Catalog entries live in models.data.models.

Error handling

import { JoyTokenAPIError, JoyTokenClient } from "@joytoken/client-sdk-ts";
const apiKey = process.env.JOY_TOKEN_API_KEY;
if (!apiKey) throw new Error("JOY_TOKEN_API_KEY is required");
const joytoken = new JoyTokenClient({ apiKey });
try {
await joytoken.chat.completions.create({
model: "auto",
messages: [{ role: "user", content: "Hello" }],
});
} catch (error) {
if (error instanceof JoyTokenAPIError) {
console.error(error.status, error.requestId, error.body);
}
throw error;
}

Run the example project

git clone https://github.com/jd-opensource/joytoken-sdk-ts.git
cd joytoken-sdk-ts
pnpm install
export JOY_TOKEN_API_KEY="sk-xxx"
cd example
pnpm live

The live example and Client SDK requests always use model: "auto"; there is no model environment variable.