Quickstart
In about ten minutes you will create a tenant, give an end user a token, let them build a personal agent and DM it with a streamed reply, and then put them in a group chat with your official agents.
Every step shows the TypeScript SDK and the equivalent raw HTTP call. The examples use these shell variables:
export AIBO_URL=https://api.heyaibo.com # or http://localhost:4000 for a local APInpm install @aibo/sdkSDK method names below follow the
@aibo/sdkv0 surface. If anything disagrees, the REST call (and the API reference) is authoritative.
1. Sign up and create an API key
Sign up in the admin console, or call the API directly. Signup creates your tenant and its first admin and returns a console session (ac_…).
import { AiboClient } from '@aibo/sdk';
const anon = new AiboClient({ baseUrl: process.env.AIBO_URL });
const session = await anon.console.signup({
email: 'founder@acme.test',
password: 'correct-horse-battery',
tenantName: 'Acme',
tenantSlug: 'acme',
});
const admin = new AiboClient({ baseUrl: process.env.AIBO_URL, consoleToken: session.token });
const key = await admin.apiKeys.create({ label: 'backend' });
console.log(key.secret); // ak_live_… — shown once, store it in your secret managercurl -sS -X POST "$AIBO_URL/v1/console/signup" \
-H 'Content-Type: application/json' \
-d '{"email":"founder@acme.test","password":"correct-horse-battery","tenantName":"Acme","tenantSlug":"acme"}'
# → 201 { "token": "ac_…", "expiresAt": "…", "tenant": {…}, "admin": {…} }
export AIBO_CONSOLE_TOKEN=ac_...
curl -sS -X POST "$AIBO_URL/v1/api-keys" \
-H "Authorization: Bearer $AIBO_CONSOLE_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"label":"backend"}'
# → 201 { "id": "…", "label": "backend", "prefix": "ak_live_3f9a", "secret": "ak_live_…", … }
export AIBO_API_KEY=ak_live_...Already have an account? Use POST /v1/console/login with { "email", "password" }. Deployments with CONSOLE_PASSWORD_LOGIN=false (the production default) take Clerk sign-in instead; see Signing users in.
2. Server: upsert a user and mint a user token
On your backend, register the signed-in user under your own id (externalId) and mint a short-lived token for them. Hand only the token to the client.
// server.ts — runs on your backend only
const server = new AiboClient({ baseUrl: process.env.AIBO_URL, apiKey: process.env.AIBO_API_KEY });
await server.users.upsert('cust_42', { displayName: 'Dana' });
const { token, expiresAt } = await server.users.createToken('cust_42', { ttlSeconds: 3600 });
// e.g. GET /api/aibo-token → { token, expiresAt } for the logged-in usercurl -sS -X PUT "$AIBO_URL/v1/users/cust_42" \
-H "Authorization: Bearer $AIBO_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"displayName":"Dana"}'
# → 201 (created) or 200 (updated) { "id": "…", "externalId": "cust_42", "displayName": "Dana", … }
curl -sS -X POST "$AIBO_URL/v1/users/cust_42/tokens" \
-H "Authorization: Bearer $AIBO_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"ttlSeconds":3600}'
# → 201 { "token": "au_…", "expiresAt": "…" }
export AIBO_USER_TOKEN=au_...ttlSeconds ranges from 60 to 86400 (default 3600). Mint a new token when it expires — the SDK's getToken option calls your function whenever it needs one.
3. Client: create a personal agent, open a DM, stream a reply
Now act as the user. The client holds only the user token.
// client.ts — browser or app
const client = new AiboClient({
baseUrl: process.env.AIBO_URL,
getToken: async () => (await fetch('/api/aibo-token').then((r) => r.json())).token,
});
// As a user, POST /v1/agents creates a personal (scope: "user") agent.
const money = await client.agents.create({
name: 'Money',
role: 'Budget coach',
bio: 'Tracks my spending and keeps me honest about my monthly budget.',
tone: 'direct',
});
console.log(money.handle); // "money" — derived from the name
// A DM has exactly one agent. Creating it again returns the existing chat.
const dm = await client.chats.create({ type: 'dm', agentIds: [money.id] });
for await (const event of client.messages.stream(dm.id, { content: 'How am I doing this month?' })) {
if (event.type === 'reply.delta') process.stdout.write(event.text);
if (event.type === 'reply.completed') process.stdout.write('\n');
}curl -sS -X POST "$AIBO_URL/v1/agents" \
-H "Authorization: Bearer $AIBO_USER_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"name":"Money","role":"Budget coach","bio":"Tracks my spending and keeps me honest about my monthly budget.","tone":"direct"}'
# → 201 { "id": "agt_money…", "scope": "user", "handle": "money", "ownerExternalId": "cust_42", … }
curl -sS -X POST "$AIBO_URL/v1/chats" \
-H "Authorization: Bearer $AIBO_USER_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"type":"dm","agentIds":["agt_money…"]}'
# → 201 (new) or 200 (existing DM) { "id": "cht_…", "type": "dm", "members": [ … ], … }
curl -sS -N -X POST "$AIBO_URL/v1/chats/cht_…/messages" \
-H "Authorization: Bearer $AIBO_USER_TOKEN" \
-H 'Content-Type: application/json' \
-H 'Accept: text/event-stream' \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"content":"How am I doing this month?"}'
# event: message.created
# data: {"type":"message.created","message":{…}}
#
# event: reply.started
# data: {"type":"reply.started","agentId":"agt_money…","replyId":"…"}
#
# event: reply.delta
# data: {"type":"reply.delta","replyId":"…","text":"You've spent"}
# …
# event: done
# data: {"type":"done"}Your backend can do the same thing without a user token by sending the API key plus X-Aibo-User: cust_42 (SDK: server.asUser('cust_42')). See Streaming for the full event sequence.
4. Group chat with tenant agents and @mention routing
Give your tenant some official agents (from the backend, as the tenant), then let the user open a group with them and their own agent.
// server.ts — as the tenant, POST /v1/agents creates tenant (scope: "tenant") agents
await server.agents.create({ name: 'Billing', role: 'Billing specialist', bio: 'Explains charges, refunds and invoices.', tone: 'warm' });
await server.agents.create({ name: 'Support', role: 'Support engineer', bio: 'Troubleshoots the Acme app.', tone: 'gentle' });// client.ts — as the user: lists tenant agents plus the user's own agents
const { data: agents } = await client.agents.list();
const ids = agents.filter((a) => ['billing', 'support', 'money'].includes(a.handle)).map((a) => a.id);
const group = await client.chats.create({ type: 'group', name: 'Sort out my bill', agentIds: ids });
// Mentioned → only @billing replies.
for await (const e of client.messages.stream(group.id, { content: '@billing why was I charged twice?' })) {
if (e.type === 'reply.started') console.log(`\n[${e.agentId}]`);
if (e.type === 'reply.delta') process.stdout.write(e.text);
}
// Unaddressed → the orchestrator picks one best-fit agent.
for await (const e of client.messages.stream(group.id, { content: 'The app keeps logging me out.' })) {
if (e.type === 'reply.delta') process.stdout.write(e.text);
}# As the tenant: create tenant agents
curl -sS -X POST "$AIBO_URL/v1/agents" \
-H "Authorization: Bearer $AIBO_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"name":"Billing","role":"Billing specialist","bio":"Explains charges, refunds and invoices.","tone":"warm"}'
curl -sS -X POST "$AIBO_URL/v1/agents" \
-H "Authorization: Bearer $AIBO_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"name":"Support","role":"Support engineer","bio":"Troubleshoots the Acme app.","tone":"gentle"}'
# As the user: see tenant agents + own agents
curl -sS "$AIBO_URL/v1/agents" -H "Authorization: Bearer $AIBO_USER_TOKEN"
# Create the group (2–7 agents)
curl -sS -X POST "$AIBO_URL/v1/chats" \
-H "Authorization: Bearer $AIBO_USER_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"type":"group","name":"Sort out my bill","agentIds":["agt_billing…","agt_support…","agt_money…"]}'
# @mention routes only to billing
curl -sS -N -X POST "$AIBO_URL/v1/chats/cht_group…/messages" \
-H "Authorization: Bearer $AIBO_USER_TOKEN" \
-H 'Content-Type: application/json' \
-H 'Accept: text/event-stream' \
-d '{"content":"@billing why was I charged twice?"}'
# Without Accept: text/event-stream you get 202 and the planned responders
curl -sS -X POST "$AIBO_URL/v1/chats/cht_group…/messages" \
-H "Authorization: Bearer $AIBO_USER_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"content":"The app keeps logging me out."}'
# → 202 { "message": {…}, "responders": ["agt_support…"] }Mention several agents (@billing @money …) and they reply one after another, in mention order. Details in Concepts → Reply routing.
Where next
- Get notified of every message server-side with Webhooks.
- Understand caps and errors in Billing & limits.