Skip to content

xAI Grok Integration

Grok conversations that survive a cold start

The xAI API is stateless: every call takes the full history. DialogueDB is where that history lives between calls, so a restart is not the end of the conversation.

Grok
DialogueDBDialogueDB

Every turn from Grok is persisted, ready to be reloaded on the next request.

How it works

Two parallel paths from the server you already have

Your OpenAI SDK client keeps calling xAI's endpoint. DialogueDB sits alongside it as a second client that stores each turn and hands it back on the next request. Nothing wraps the model call.

Your server

OpenAI SDK client+DialogueDB client

Model call

chat.completions.create

Runs directly to Grok. Nothing wraps it.

Persistence

saveMessage · loadMessages

Each turn stored, reloaded on the next request.

What else DialogueDB adds

Three more things a Grok app runs into next

All handled by the same client. No second service, no vector database.

Every user gets their own conversation

Pass a namespace when you save and when you read. Two users share the same dialogue id and never see each other, with no tenant column in your app.

Returning users do not start over

Facts worth keeping live as Memory objects, separate from any single conversation. The next session with the same user opens with what you chose to remember.

Answer follow-ups from earlier sessions

Search past exchanges by meaning to pull the right context into the next call. No embeddings pipeline, no vector database to run alongside your app.

Nothing extra to write

OpenAI-compatible, so the mapping is a pass-through

The shape DialogueDB stores is the shape Grok's API takes. Three things you'd otherwise have to write, that you don't.

Compatibility check

OpenAI SDK · xAI endpoint

3 of 3 pass
  • Role names

    system, user, and assistant map straight through — no assistant-to-model swap either direction.

  • Content shape

    Stored content is a string. Grok takes a string. No blocks, no parts arrays, no wire-format translation.

  • Client library

    The OpenAI SDK you already have works pointed at https://api.x.ai/v1. Nothing wraps the model call.

Ready — no adapter class required

The same client does more

Six more things the same install covers, without another SDK to add.

Continue a conversation on any worker

Load the dialogue by id and the message array rebuilds from scratch, on whichever instance takes the next request.

dialogue.loadMessages({ order: "asc" })

Facts and preferences across sessions

Store what the assistant should remember about a user, independent of any single conversation.

db.createMemory()

Recall by meaning before you call Grok

Search stored memories against the new turn and put the matches in the system message.

db.searchMemories()

Token counts alongside the message

Keep usage.total_tokens on the message it belongs to, so cost per conversation is a query rather than a guess.

metadata: { totalTokens }

Branch a conversation for a sub-task

Parent and child dialogues let a delegated task keep its own transcript without polluting the main thread.

dialogue.createThread()

Per-conversation scratchpad

Give each conversation its own state object for workflow flags, partial results, or in-progress context.

dialogue.saveState()

Install in 3 steps

From npm install to the first persisted turn, in under five minutes.

1

Install the client

npm install dialogue-db
2

Set both API keys

DIALOGUE_DB_API_KEY=...
XAI_API_KEY=...
3

Save each turn

await dialogue.saveMessage({
  role: "assistant",
  content: reply,
})
Get Your Free API Key

No credit card. Free tier to start. Starter is $29/month when usage grows.

The reference example

One turn, a cold reload, then a second turn continued from the reloaded history.

Runs on Edge and serverless too. The client is HTTP-only, with no connection pool and no native dependencies.

src/index.ts
import OpenAI from "openai"
import { DialogueDB } from "dialogue-db"
import { toChatMessages, loadDialogue } from "./persist"
 
const db = new DialogueDB({ apiKey: process.env.DIALOGUE_DB_API_KEY! })
const xai = new OpenAI({
apiKey: process.env.XAI_API_KEY,
baseURL: "https://api.x.ai/v1",
})
 
/** One turn: persist the user message, run Grok, persist the reply. */
async function runTurn(dialogue: Dialogue, userText: string) {
await dialogue.saveMessage({ role: "user", content: userText })
 
const response = await xai.chat.completions.create({
model: MODEL,
messages: toChatMessages(dialogue),
})
const reply = response.choices[0].message.content ?? ""
 
await dialogue.saveMessage({ role: "assistant", content: reply })
return reply
}
 
// A fresh process starts here, holding nothing.
const reloaded = await loadDialogue(db, dialogueId, NAMESPACE)
await runTurn(reloaded, "And in one word, why does that matter?")

Hover a step to see the lines it maps to.

Reference example

xai

The whole bridge is two functions in src/persist.ts: one maps a loaded dialogue to the message array the xAI API takes, the other loads a dialogue with its messages in order. Every read and write is scoped to a namespace.

Open the example

Where DialogueDB fits

Four ways to keep a Grok conversation, and what each one asks of you.

ApproachSurvives a restartPortable across providersMemory and search included
In-process arrayNoYes, until the process endsNo
Your own tableYesYes, once you design the schemaAdd a vector store and a pipeline
Standalone memory serviceYes, for extracted factsUsuallyMemory yes, full transcript no
DialogueDBYesYes, role and content onlyYes, one API

If you know chat completions, you already know this

Everything nests under a namespace, and the concepts line up with the message array you already build.

The data model

namespace

scoped to one user or tenant

dialogue

messages, threads, state

indexed for semantic search

memory

facts and preferences, cross-session

indexed for semantic search

xAI chat completionsDialogueDB
messages: ChatCompletionMessageParam[] you pass to chat.completions.createdialogue.loadMessages({ order: 'asc' }) rebuilds it
ChatCompletionMessageParammessage, stored as sent
role: 'system' | 'user' | 'assistant'Same role field on stored messages, unchanged
content stringSame content field, stored as sent
The system prompt messageComposed from db.searchMemories() at request time
usage.prompt_tokens, usage.completion_tokensmetadata on the assistant message

A stored message is a role and a content value, which is exactly what an OpenAI-compatible endpoint takes. That is why the reference mapper is a few lines long and why the same stored conversation is not tied to one provider.

What DialogueDB sees

Only what's needed for conversation storage

Your call to Grok goes straight from your server to xAI. DialogueDB sits beside that request, never inside it.

The client only receives what you explicitly send: message content, memory values, and the identifiers you scope them with. Your xAI key, your user tokens, and everything else in your app never pass through it.

What we receive

  • Message content and roles
  • Memory values and metadata
  • Dialogue and namespace IDs

What we never see

  • Your xAI API key
  • User authentication tokens
  • Your xAI base URL, system prompts, or model settings

Two independent calls from your server

Your server

src/turn.ts

Your code decides what goes where.

nothing routed through us

xAI

api.x.ai/v1

The model call goes direct. DialogueDB never sees the request or the credential.

DialogueDB

Messages and memory only

Receives what you explicitly save. Nothing implicit, nothing extra.

Frequently asked questions

One API for messages, memory, and search

Add it to your Grok app in minutes.