JavaScript / TypeScript
All examples use fetch (Node 18+ or any modern browser) and read the key from
the environment. They are runnable as-is against the live API.
const BASE = "https://ghostmind.optdmsa.com";const AUTH = { Authorization: `Bearer ${process.env.GHOSTMIND_API_KEY}` };const JSON_HEADERS = { ...AUTH, "Content-Type": "application/json" };Chat Completions
const response = await fetch(`${BASE}/v1/chat/completions`, { method: "POST", headers: JSON_HEADERS, body: JSON.stringify({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], }),});
const data = await response.json();console.log(data.choices[0].message.content);// The conversation id for follow-up messages:const convId = response.headers.get("x-ghostmind-conversation-id");Streaming
const response = await fetch(`${BASE}/v1/chat/completions`, { method: "POST", headers: JSON_HEADERS, body: JSON.stringify({ model: "gpt-4o", stream: true, messages: [{ role: "user", content: "Count to five." }], }),});
const reader = response.body!.getReader();const decoder = new TextDecoder();
while (true) { const { done, value } = await reader.read(); if (done) break; process.stdout.write(decoder.decode(value)); // SSE data: lines}Durable conversation
Capture the conversation id once, continue anytime — same upstream thread.
const first = await fetch(`${BASE}/v1/chat/completions`, { method: "POST", headers: JSON_HEADERS, body: JSON.stringify({ model: "gpt-4o", messages: [{ role: "user", content: "Remember: the invoice prefix is RT-." }], }),});const convId = first.headers.get("x-ghostmind-conversation-id");
const later = await fetch(`${BASE}/v1/chat/completions`, { method: "POST", headers: { ...JSON_HEADERS, "X-GhostMind-Conversation-Id": convId!, }, body: JSON.stringify({ model: "gpt-4o", messages: [{ role: "user", content: "What is the invoice prefix?" }], }),});Project-scoped chat with a source file
The upstream-project slug (not the UUID) goes in
X-GhostMind-Upstream-Project.
const proj = await ( await fetch(`${BASE}/v1/projects`, { method: "POST", headers: JSON_HEADERS, body: JSON.stringify({ name: "My Engine", instructions: "Always answer in JSON.", memory_policy: "project_only", }), })).json();const { slug, id: projId } = proj;
// Multipart upload — do NOT set Content-Type; fetch sets the boundary.const form = new FormData();form.append("file", new Blob([fileBytes], { type: "text/plain" }), "brand_voice.txt");const file = await ( await fetch(`${BASE}/v1/projects/${projId}/files`, { method: "POST", headers: AUTH, body: form, })).json();
// Wait until the file is indexed ("ready") — required for retrieval.while (true) { const detail = await ( await fetch(`${BASE}/v1/projects/${projId}`, { headers: AUTH }) ).json(); const status = detail.sources.find((s: any) => s.id === file.id)?.status; if (status === "ready") break; if (status === "failed") throw new Error("file indexing failed"); await new Promise((r) => setTimeout(r, 2000));}
const answer = await fetch(`${BASE}/v1/chat/completions`, { method: "POST", headers: { ...JSON_HEADERS, "X-GhostMind-Upstream-Project": slug, }, body: JSON.stringify({ model: "gpt-4o", messages: [{ role: "user", content: "Summarize the brand voice file." }], }),});Async speech generation
Speech takes ~30–120 s upstream — always use the job endpoint, never the bounded sync path for real workloads.
const job = await ( await fetch(`${BASE}/v1/audio/generations`, { method: "POST", headers: JSON_HEADERS, body: JSON.stringify({ input: "مرحباً بك في منصتنا", voice: "savio_default", response_format: "wav", }), })).json(); // 202 Accepted
while (true) { const { status } = await ( await fetch(`${BASE}/v1/audio/generations/${job.id}`, { headers: AUTH }) ).json(); if (status === "completed") break; if (status === "failed" || status === "cancelled") throw new Error(`job ${status}`); await new Promise((r) => setTimeout(r, 5000));}
const audio = await fetch(`${BASE}/v1/audio/generations/${job.id}/content`, { headers: AUTH,});const wav = Buffer.from(await audio.arrayBuffer());// Generated content expires after 24 h by default// (operator-configurable via GHOSTMIND_AUDIO_JOB_TTL_HOURS).Transcription
const form = new FormData();form.append("file", new Blob([wav], { type: "audio/wav" }), "clip.wav");form.append("model", "whisper-1");
const result = await ( await fetch(`${BASE}/v1/audio/transcriptions`, { method: "POST", headers: AUTH, // no Content-Type — fetch sets the multipart boundary body: form, })).json();console.log(result.text); // usually < 2 s for short clipsUsage
const usage = await ( await fetch(`${BASE}/v1/usage?period=7d`, { headers: AUTH })).json();console.log(usage.total_requests, usage.total_tokens_in, usage.total_tokens_out);// Scoped to THIS api key — never other tenants.Next Steps
- Integration Cookbook — full journeys with every error code and gotcha
- OpenAI SDK Compatibility — drop-in client
- Errors Reference — the canonical error envelope