usage, cost, and a gateway requestId on every
response. It uses the first-party @roadie/sdk.
The finished code mirrors the runnable examples/node-chat reference app,
which is exercised against a live gateway in the repo’s CI smoke gate — so what you copy here is
proven against the real wire contract.
Prefer to keep your existing OpenAI code? See the
OpenAI drop-in tutorial instead. This page adopts the
Roadie SDK for its typed errors, retries, and normalized responses.
Prerequisites
In the dashboard:- Create a project (its
developmentandproductionenvironments are created for you). - Add and validate a provider credential (OpenAI or Anthropic — BYOK). See Providers & BYOK.
- Create a secret key (
rd_sk_…) for the environment you want to call.
1. Scaffold the project
.env (never commit it):
.env
2. Construct the client once
Build the client from the environment and reuse it. ProvidingbaseUrl is optional — omit it for
the default hosted gateway ().
src/roadie.ts
3. A non-streaming completion
chat.create returns the normalized response: the assistant message, finish_reason, token
usage, an estimated cost, and the gateway requestId. Attribute the call to one of your
end users with user so per-user limits, quotas, and analytics apply.
src/chat.ts
4. Stream tokens to the caller
Iterate the async-iterable stream withfor await. content_delta events carry incremental text;
the terminal message_end event carries the final usage. The onDelta callback is where you
forward each chunk — to stdout, a WebSocket, or an HTTP ReadableStream.
src/stream.ts
{ signal, timeoutMs }; pass an AbortSignal to cancel.
See Streaming.
5. Create embeddings
embeddings.create accepts a string or an array and returns one vector per input, in order.
src/embeddings.ts
6. Handle failures by type
Every SDK failure is aRoadieError subclass carrying a stable code, the requestId, the HTTP
status, and — for rate limits — retryAfter. Branch on the class and react appropriately.
src/errors.ts
408/429/5xx) before they reach
you, honoring Retry-After. So a RateLimitError in your catch means retries were already
exhausted. See Errors & retries and the full
error reference.
7. Collect feedback on a response
Use therequestId from a completion to record a thumbs-up/down — the same id shows up in your
request logs, so feedback and the request line up.
src/feedback.ts
8. Wire it into a server route
The functions above are transport-agnostic. In a Next.js App Router route you bridge the SDK’s stream onto aReadableStream so the browser reads tokens as they arrive, while the secret key
never leaves the server:
app/api/chat/route.ts
What you built
You built a server-side AI backend on@roadie/sdk: a reusable client, non-streaming and
streaming chat, embeddings, feedback, and error handling that branches on typed classes — with
every call attributed to one of your end users. Your secret key stays server-side, retries and
idempotency are automatic, and each response carries the usage, cost, and requestId you need
for billing and support.
Next steps
Per-user limits
Turn
user: { id } attribution into per-user rate limits and quotas.Cross-provider fallback
Route
model: 'smart' through an alias that fails over across providers.Observability
Read the usage, cost, and logs these calls produce.
TypeScript SDK reference
The full client surface: options, resources, streaming, and token providers.