Get Started
Quickstart
From zero to a streaming agent response in five minutes.
This walks through the fastest path to a working agent run. We'll define a simple example skill and send it a message.
1. Install (1 min)
git clone https://github.com/belulok/relay-e.git && cd relay-e
nvm use
npm install
cp .env.example .envEdit .env and add one provider key:
DEV_API_KEY=sk_dev_local_change_me
OPENROUTER_API_KEY=sk-or-v1-...2. Boot the stack (1 min)
npm run stack:up
npm run db:generate && npm run db:migrate
npm run devThe API is up at http://localhost:3001. Open /docs for the interactive Scalar UI.
3. Send a sync message (1 min)
curl -s -X POST http://localhost:3001/v1/sessions/demo/messages \
-H "Authorization: Bearer sk_dev_local_change_me" \
-H "Content-Type: application/json" \
-d '{
"prompt": "How much did I spend on food last month?",
"skills": ["financial-advisor"]
}' | jqYou'll get back something like:
{
"id": "msg_...",
"session_id": "demo",
"role": "assistant",
"content": [{ "type": "text", "text": "Last month you spent..." }],
"usage": { "tokens_in": 1234, "tokens_out": 215, "cost_usd": 0.003 },
"finish_reason": "stop",
"tool_calls": [
{ "name": "analyze_spending", "input": { "days": 30 }, "output": {...} }
],
"context": { "sources": ["skill_guardrails"] }
}The agent decided to call analyze_spending, got back data, and used it to answer. You wrote zero orchestration code.
4. Stream it (1 min)
curl -N -X POST http://localhost:3001/v1/sessions/demo/messages \
-H "Authorization: Bearer sk_dev_local_change_me" \
-H "Content-Type: application/json" \
-d '{
"prompt": "Show my balance and top spending categories.",
"skills": ["financial-advisor"],
"stream": true
}'You'll see SSE events flow in:
event: thinking
data: {"step":"resolving_context"}
event: context_resolved
data: {"sources":["skill_guardrails"],"tokenEstimate":12}
event: tool_call
data: {"tool_call_id":"tc_...","name":"get_balance","input":{...}}
event: tool_result
data: {"tool_call_id":"tc_...","name":"get_balance","output":{...}}
event: usage
data: {"usage":{"tokens_in":2103,"tokens_out":312,"cost_usd":0.005},"model":"claude-sonnet-4-6"}
event: text
data: {"text":"Your balance is $4,231.55..."}5. Add your own skill (1 min)
Open apps/api/src/bootstrap/registries.ts:
tools.register(defineTool({
name: "get_weather",
description: "Return current weather for a city.",
inputSchema: z.object({ city: z.string() }),
execute: async ({ city }) => ({ city, temp_c: 22, condition: "sunny" }),
}));
skills.register(defineSkill({
name: "weather-bot",
description: "Answers questions about the weather.",
systemPrompt: "You are a helpful weather assistant.",
toolNames: ["get_weather"],
preferredTier: "fast",
}));Restart npm run dev, then:
curl -X POST http://localhost:3001/v1/sessions/demo/messages \
-H "Authorization: Bearer sk_dev_local_change_me" \
-d '{"prompt":"What is the weather in Tokyo?","skills":["weather-bot"]}'The new skill is auto-listed in /v1/skills, the new tool in /v1/tools, and both appear in /openapi.json and the /docs UI without any restart of those pages.

