SmoothAgent API
Base URL: https://agent.smoothcodes.com
Quick Start
Get from zero to a working AI agent in 4 steps.
Step 1: Create an account
curl -X POST https://agent.smoothcodes.com/auth/signup \
-H "Content-Type: application/json" \
-d '{"email": "you@example.com", "password": "your-password-here"}'
Response:
{
"token": "eyJhbGciOi...",
"devId": "dev_a1b2c3d4e5f6",
"email": "you@example.com",
"verified": true
}
Save the token value. This is your DEV_TOKEN (valid for 7 days).
Step 2: Create an agent
curl -X POST https://agent.smoothcodes.com/dashboard/configs \
-H "Content-Type: application/json" \
-H "Authorization: Bearer DEV_TOKEN" \
-d '{
"llmProvider": "anthropic",
"llmApiKey": "sk-ant-api03-your-key-here",
"mcpUrl": "https://your-mcp-server.example.com/mcp",
"systemPrompt": "You are a helpful assistant.",
"themeName": "My Agent"
}'
Response:
{ "id": "cfg_a1b2c3d4e5f67890" }
Save the id value. This is your CONFIG_ID.
Step 3: Generate a user token
curl -X POST https://agent.smoothcodes.com/dashboard/configs/CONFIG_ID/token \
-H "Content-Type: application/json" \
-H "Authorization: Bearer DEV_TOKEN" \
-d '{"userId": "user_123", "expiresInDays": "30"}'
Response:
{
"token": "eyJhbGciOi...",
"configId": "cfg_a1b2c3d4e5f67890",
"userId": "user_123",
"expiresIn": "30 days"
}
This USER_TOKEN is what your end-users use to chat.
Step 4: Send a message
First, create a chat:
curl -X POST https://agent.smoothcodes.com/chats \
-H "Content-Type: application/json" \
-H "Authorization: Bearer USER_TOKEN" \
-d '{"title": "My first chat"}'
Response:
{
"id": "chat_a1b2c3d4e5f6",
"configId": "cfg_a1b2c3d4e5f67890",
"userId": "user_123",
"title": "My first chat",
"messages": []
}
Then, send a prompt (SSE stream):
curl -N -X POST https://agent.smoothcodes.com/chats/CHAT_ID/prompt \
-H "Content-Type: application/json" \
-H "Authorization: Bearer USER_TOKEN" \
-d '{"content": "Hello, what can you do?"}'
Response headers include X-Prompt-Id, X-Chat-Id, and X-Request-Id.
Response body is an SSE stream:
data: {"type":"text-delta","delta":"Hello"}
data: {"type":"text-delta","delta":"! I"}
data: {"type":"text-delta","delta":" can help"}
data: {"type":"text-delta","delta":" you with..."}
data: {"type":"finish","finishReason":"stop"}
data: [DONE]
Authentication
SmoothAgent uses JWT Bearer tokens. Include the token in the Authorization header:
Authorization: Bearer YOUR_TOKEN
Token Types
| Type | Scope | Expiry | Use case |
|---|---|---|---|
| Dev | dev | 7 days | Dashboard access, agent management, usage viewing |
| Admin | admin, chat | 1 year | Backend automation, token generation, server-to-server |
| User | chat | 1-365 days (configurable) | End-user chat access, scoped to one agent |
| Guest | dev | 90 days | Auto-created anonymous accounts, convertible to real accounts |
Typical Flow
- Developer signs up and gets a DEV_TOKEN
- Developer creates agents via the Dashboard API using the DEV_TOKEN
- Developer generates an ADMIN_TOKEN for their backend
- Backend uses ADMIN_TOKEN to generate USER_TOKENs for end-users
- End-users chat using their USER_TOKEN (scoped to one agent)
SDK
Install the SDK:
npm install smoothagent
Constructor
import { SmoothAgent } from 'smoothagent';
const agent = new SmoothAgent({
token: 'USER_TOKEN', // User JWT for chat access
adminToken: 'ADMIN_TOKEN', // Optional: admin JWT for management
workerUrl: 'https://agent.smoothcodes.com', // Optional: defaults to window.location.origin
});
Auth Methods
// Sign up
const { token, devId, email } = await agent.signup('you@example.com', 'password123');
// Log in
const { token, devId, email, plan } = await agent.login('you@example.com', 'password123');
// Get current user info
const me = await agent.me();
Chat Methods
// Create a chat
const chat = await agent.createChat('My Chat Title');
// List all chats
const chats = await agent.listChats();
// Get a specific chat with messages
const chatWithMessages = await agent.getChat('chat_abc123');
// Delete a chat
await agent.deleteChat('chat_abc123');
Prompt (Callback Style)
const result = await agent.prompt(chat.id, 'Explain quantum computing', {
onDelta: (delta, fullText) => {
// Partial text token received
process.stdout.write(delta);
},
onThinking: (delta, fullThinking) => {
// Extended thinking/reasoning token
console.log('[thinking]', delta);
},
onToolCall: ({ toolCallId, toolName }) => {
// Tool was called
console.log('Tool called:', toolName);
},
onToolResult: ({ toolCallId, result }) => {
// Tool returned a result
console.log('Tool result:', result);
},
onFinish: ({ text, reason }) => {
// Stream completed
console.log('Done:', reason);
},
onError: (errorMessage) => {
// Error occurred
console.error('Error:', errorMessage);
},
config: {
// Optional: override agent config for this prompt
llmModel: 'claude-haiku-4-5-20251001',
temperature: '0.5',
},
});
// result: { text, promptId, usage, finishReason, steps }
Prompt (Async Iterator)
for await (const event of agent.stream(chat.id, 'Tell me a story')) {
switch (event.type) {
case 'text-delta':
process.stdout.write(event.delta);
break;
case 'thinking-delta':
console.log('[thinking]', event.delta);
break;
case 'tool-call':
console.log('Tool:', event.toolName);
break;
case 'tool-result':
console.log('Result:', event.result);
break;
case 'finish':
console.log('\nDone:', event.reason, 'Usage:', event.usage);
break;
case 'error':
console.error('Error:', event.message);
break;
}
}
Stateless Chat (No History)
const result = await agent.chat(
[{ role: 'user', content: 'What is 2+2?' }],
{
onDelta: (delta) => process.stdout.write(delta),
config: {
llmProvider: 'anthropic',
llmApiKey: 'sk-ant-api03-...',
llmModel: 'claude-haiku-4-5-20251001',
},
}
);
Agent Management
// Create agent
const { id } = await agent.createAgent({
llmProvider: 'anthropic',
llmApiKey: 'sk-ant-api03-...',
mcpUrl: 'https://mcp.example.com/mcp',
systemPrompt: 'You are helpful.',
themeName: 'Support Bot',
});
// List agents
const agents = await agent.listAgents();
// Update agent
await agent.updateAgent('cfg_abc123', { systemPrompt: 'Updated prompt' });
// Delete agent
await agent.deleteAgent('cfg_abc123');
// Generate user token
const { token } = await agent.createUser('user_456', { configId: 'cfg_abc123' });
Usage and Health
// Get usage stats
const usage = await agent.getUsage('cfg_abc123', 7); // last 7 days
Cancel Running Prompt
// Cancel is automatic via AbortController in the SDK's fetch retry logic.
// From the API, you can also:
await fetch(`${BASE}/chats/${chatId}/prompt`, {
method: 'DELETE',
headers: { 'Authorization': 'Bearer USER_TOKEN' },
});
Auth API
/auth/signup
Create a new developer account. Optionally converts a guest account.
Request
curl -X POST https://agent.smoothcodes.com/auth/signup \
-H "Content-Type: application/json" \
-d '{
"email": "dev@example.com",
"password": "securepass123",
"guestId": "dev_guest123"
}'
| Field | Type | Required | Description |
|---|---|---|---|
| string | Yes | Valid email address | |
| password | string | Yes | Minimum 8 characters |
| guestId | string | No | Guest dev ID to convert (preserves chats and configs) |
Response 201 Created
{
"token": "eyJhbGciOi...",
"devId": "dev_a1b2c3d4e5f6",
"email": "dev@example.com",
"chatToken": "eyJhbGciOi...",
"verified": true
}
Errors
| Status | Error | Cause |
|---|---|---|
| 400 | Invalid email | Email format is invalid |
| 400 | Password must be at least 8 characters | Password too short |
| 400 | Invalid guest session | guestId does not exist or is not a guest |
| 409 | Email already registered | Account with this email exists |
/auth/login
Log in with email and password. Optionally transfers guest chats to the account.
Request
curl -X POST https://agent.smoothcodes.com/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "dev@example.com",
"password": "securepass123",
"guestId": "dev_guest123"
}'
| Field | Type | Required | Description |
|---|---|---|---|
| string | Yes | Registered email address | |
| password | string | Yes | Account password |
| guestId | string | No | Guest dev ID whose chats/configs will be transferred |
Response 200 OK
{
"token": "eyJhbGciOi...",
"devId": "dev_a1b2c3d4e5f6",
"email": "dev@example.com",
"plan": "pro",
"chatToken": "eyJhbGciOi..."
}
Errors
| Status | Error | Cause |
|---|---|---|
| 400 | Invalid email | Email format is invalid |
| 400 | Password must be at least 8 characters | Password too short |
| 401 | Invalid email or password | Credentials do not match |
| 403 | Email not verified. Check your inbox. | Account exists but email not verified |
| 429 | Too many attempts. Try again in N minutes. | Brute force lockout (5 attempts, 15min cooldown) |
/auth/me
Get the currently authenticated developer's profile.
Request
curl https://agent.smoothcodes.com/auth/me \
-H "Authorization: Bearer DEV_TOKEN"
Response 200 OK
{
"id": "dev_a1b2c3d4e5f6",
"email": "dev@example.com",
"plan": "pro",
"createdAt": "2025-01-15T10:30:00.000Z"
}
Errors
| Status | Error | Cause |
|---|---|---|
| 401 | Authentication required | Missing or invalid Bearer token |
| 404 | Dev not found | Account was deleted |
/auth/guest
Create an anonymous guest account. No email or password needed. Guest accounts can later be converted to real accounts via signup.
Request
curl -X POST https://agent.smoothcodes.com/auth/guest
Response 201 Created
{
"token": "eyJhbGciOi...",
"devId": "dev_g1h2i3j4k5l6",
"guest": true
}
/auth/forgot-password
Request a password reset email. Always returns success to avoid revealing whether an email is registered.
Request
curl -X POST https://agent.smoothcodes.com/auth/forgot-password \
-H "Content-Type: application/json" \
-d '{"email": "dev@example.com"}'
| Field | Type | Required | Description |
|---|---|---|---|
| string | Yes | Account email address |
Response 200 OK
{
"ok": true,
"message": "If that email exists, a reset link was sent."
}
/auth/reset-password
Reset password using a token received via email. Tokens expire after 1 hour.
Request
curl -X POST https://agent.smoothcodes.com/auth/reset-password \
-H "Content-Type: application/json" \
-d '{
"token": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"newPassword": "newsecurepass456"
}'
| Field | Type | Required | Description |
|---|---|---|---|
| token | string | Yes | Reset token from the email link |
| newPassword | string | Yes | New password (minimum 8 characters) |
Response 200 OK
{
"ok": true,
"message": "Password updated"
}
Errors
| Status | Error | Cause |
|---|---|---|
| 400 | Invalid or missing token | Token is empty or malformed |
| 400 | Password must be at least 8 characters | New password too short |
| 400 | Invalid or expired reset token | Token has expired (1h) or was already used |
Dashboard API
All Dashboard endpoints require a DEV_TOKEN in the Authorization header.
/dashboard/configs
List all agents (configs) belonging to the authenticated developer.
Request
curl https://agent.smoothcodes.com/dashboard/configs \
-H "Authorization: Bearer DEV_TOKEN"
Response 200 OK
{
"configs": [
{
"id": "cfg_a1b2c3d4e5f67890",
"mcpUrl": "https://mcp.example.com/mcp",
"mcpServers": [{"name": "agent", "url": "https://mcp.example.com/mcp"}],
"llmProvider": "anthropic",
"llmModel": "claude-sonnet-4-20250514",
"llmBaseUrl": null,
"themeName": "Support Bot",
"themeColor": "#6366f1",
"systemPrompt": "You are a helpful assistant.",
"allowedOrigins": null,
"hasMcpToken": 0,
"plan": "pro",
"maxHistory": null,
"maxTokens": null,
"temperature": null,
"maxSteps": 15,
"maxBudgetUsd": null,
"webSearch": 0,
"webhookUrl": null,
"createdAt": "2025-01-15T10:30:00.000Z",
"updatedAt": "2025-01-15T12:00:00.000Z"
}
]
}
Errors
| Status | Error | Cause |
|---|---|---|
| 401 | Login required | Missing or invalid DEV_TOKEN |
/dashboard/configs
Create a new agent. All fields are optional; configure what you need.
Request
curl -X POST https://agent.smoothcodes.com/dashboard/configs \
-H "Content-Type: application/json" \
-H "Authorization: Bearer DEV_TOKEN" \
-d '{
"llmProvider": "anthropic",
"llmApiKey": "sk-ant-api03-your-key",
"llmModel": "claude-sonnet-4-20250514",
"mcpUrl": "https://mcp.example.com/mcp",
"mcpToken": "Bearer mcp-secret",
"systemPrompt": "You are a helpful support agent.",
"themeName": "Support Bot",
"themeColor": "#6366f1",
"maxSteps": 10,
"maxHistory": 50,
"temperature": 0.7,
"webSearch": true
}'
See Agent Config Fields for all available fields.
Response 201 Created
{ "id": "cfg_a1b2c3d4e5f67890" }
Errors
| Status | Error | Cause |
|---|---|---|
| 401 | Login required | Missing or invalid DEV_TOKEN |
/dashboard/configs/:id
Update an existing agent. Only send the fields you want to change.
Request
curl -X PUT https://agent.smoothcodes.com/dashboard/configs/cfg_a1b2c3d4e5f67890 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer DEV_TOKEN" \
-d '{
"systemPrompt": "Updated system prompt.",
"maxTokens": 4096,
"temperature": 0.3
}'
Response 200 OK
{ "ok": true }
Errors
| Status | Error | Cause |
|---|---|---|
| 400 | Nothing to update | Request body has no recognized fields |
| 401 | Login required | Missing or invalid DEV_TOKEN |
| 404 | Config not found | Config does not exist or is not owned by this dev |
/dashboard/configs/:id
Delete an agent permanently.
Request
curl -X DELETE https://agent.smoothcodes.com/dashboard/configs/cfg_a1b2c3d4e5f67890 \
-H "Authorization: Bearer DEV_TOKEN"
Response 200 OK
{ "ok": true }
Errors
| Status | Error | Cause |
|---|---|---|
| 401 | Login required | Missing or invalid DEV_TOKEN |
| 404 | Config not found | Config does not exist or is not owned by this dev |
/dashboard/configs/:id/token
Generate a user token for end-user chat access, scoped to one agent.
Request
curl -X POST https://agent.smoothcodes.com/dashboard/configs/cfg_a1b2c3d4e5f67890/token \
-H "Content-Type: application/json" \
-H "Authorization: Bearer DEV_TOKEN" \
-d '{
"userId": "user_abc123",
"expiresInDays": "30"
}'
| Field | Type | Required | Description |
|---|---|---|---|
| userId | string | No | Custom user ID. Auto-generated if omitted (user_xxx). |
| expiresInDays | string | No | Token lifetime in days (1-365, default: 365) |
Response 201 Created
{
"token": "eyJhbGciOi...",
"configId": "cfg_a1b2c3d4e5f67890",
"userId": "user_abc123",
"expiresIn": "30 days"
}
Errors
| Status | Error | Cause |
|---|---|---|
| 401 | Login required | Missing or invalid DEV_TOKEN |
| 404 | Config not found | Config does not exist or is not owned by this dev |
/dashboard/usage
Get usage statistics for an agent or all agents.
Request
curl "https://agent.smoothcodes.com/dashboard/usage?configId=cfg_a1b2c3d4e5f67890&days=7" \
-H "Authorization: Bearer DEV_TOKEN"
| Param | Type | Required | Description |
|---|---|---|---|
| configId | string | No | Filter by agent. If omitted, returns all agents' usage. |
| days | number | No | Number of days to look back (default: 7) |
Response 200 OK
{
"totalPrompts": 142,
"totalTokensIn": 50000,
"totalTokensOut": 120000,
"totalToolCalls": 35,
"daily": [
{ "date": "2025-01-15", "prompts": 20, "tokensIn": 7000, "tokensOut": 17000 }
]
}
Errors
| Status | Error | Cause |
|---|---|---|
| 401 | Login required | Missing or invalid DEV_TOKEN |
| 404 | Config not found | configId is not owned by this dev |
/dashboard/chats
List all end-user chats across the developer's agents.
Request
curl "https://agent.smoothcodes.com/dashboard/chats?limit=20" \
-H "Authorization: Bearer DEV_TOKEN"
| Param | Type | Required | Description |
|---|---|---|---|
| limit | number | No | Maximum chats to return (default: 50) |
Response 200 OK
{
"chats": [
{
"id": "chat_a1b2c3d4e5f6",
"configId": "cfg_a1b2c3d4e5f67890",
"userId": "user_abc123",
"title": "Help with billing",
"configName": "Support Bot",
"createdAt": "2025-01-15T10:30:00.000Z",
"updatedAt": "2025-01-15T12:00:00.000Z"
}
]
}
Errors
| Status | Error | Cause |
|---|---|---|
| 401 | Login required | Missing or invalid DEV_TOKEN |
/dashboard/mcp-health
Check the health of an agent's MCP server.
Request
curl "https://agent.smoothcodes.com/dashboard/mcp-health?configId=cfg_a1b2c3d4e5f67890" \
-H "Authorization: Bearer DEV_TOKEN"
| Param | Type | Required | Description |
|---|---|---|---|
| configId | string | Yes | Agent ID to check |
Response 200 OK (online)
{
"status": "online",
"latency": 145,
"serverInfo": { "name": "my-mcp-server", "version": "1.0" },
"tools": "available"
}
Response 200 OK (offline)
{
"status": "offline",
"latency": 3000,
"error": "unreachable"
}
Errors
| Status | Error | Cause |
|---|---|---|
| 400 | configId required | Missing configId query parameter |
| 401 | Login required | Missing or invalid DEV_TOKEN |
| 404 | Config not found | Config does not exist or is not owned by this dev |
/dashboard/configs/:id/plan
Change the plan for an agent.
Request
curl -X PUT https://agent.smoothcodes.com/dashboard/configs/cfg_a1b2c3d4e5f67890/plan \
-H "Content-Type: application/json" \
-H "Authorization: Bearer DEV_TOKEN" \
-d '{"plan": "pro"}'
| Field | Type | Required | Description |
|---|---|---|---|
| plan | string | Yes | One of: free, pro, enterprise |
Response 200 OK
{ "ok": true, "plan": "pro" }
Errors
| Status | Error | Cause |
|---|---|---|
| 400 | Invalid plan | Plan is not free, pro, or enterprise |
| 401 | Login required | Missing or invalid DEV_TOKEN |
| 404 | Not found | Config does not exist or is not owned by this dev |
Billing API
Billing is handled through Stripe. All endpoints require a DEV_TOKEN.
Plans
| Plan | Price | Included Concurrent | Description |
|---|---|---|---|
| Free | $0/mo | 1 | For testing and personal projects |
| Pro | $5/mo | 10 | For production apps, unlimited agents |
| Business | $19/mo | 100 | For growing teams and apps |
| Enterprise | $99/mo | 1,000+ | Isolated D1 database, dedicated VPS option |
/billing/checkout
Create a Stripe Checkout session for subscribing to a plan.
Request
curl -X POST https://agent.smoothcodes.com/billing/checkout \
-H "Content-Type: application/json" \
-H "Authorization: Bearer DEV_TOKEN" \
-d '{"plan": "pro"}'
| Field | Type | Required | Description |
|---|---|---|---|
| plan | string | No | Plan to subscribe to: pro or enterprise (default: pro) |
Response 200 OK
{
"url": "https://checkout.stripe.com/c/pay/cs_live_...",
"sessionId": "cs_live_..."
}
Redirect the user to the url to complete payment.
Errors
| Status | Error | Cause |
|---|---|---|
| 400 | Invalid plan. Use: pro or enterprise | Unrecognized plan name |
| 401 | Login required | Missing or invalid DEV_TOKEN |
| 404 | Dev not found | Account was deleted |
/billing/portal
Get a URL to the Stripe Customer Portal where the user can manage their subscription.
Request
curl https://agent.smoothcodes.com/billing/portal \
-H "Authorization: Bearer DEV_TOKEN"
Response 200 OK
{
"url": "https://billing.stripe.com/p/session/..."
}
Redirect the user to the url to manage billing.
Errors
| Status | Error | Cause |
|---|---|---|
| 400 | No billing account. Subscribe first. | No Stripe customer on file |
| 401 | Login required | Missing or invalid DEV_TOKEN |
Chats API
Chats endpoints require a USER_TOKEN (or DEV_TOKEN for dev-owned chats).
/chats
Create a new chat session.
Request
curl -X POST https://agent.smoothcodes.com/chats \
-H "Content-Type: application/json" \
-H "Authorization: Bearer USER_TOKEN" \
-d '{"title": "Help with billing"}'
| Field | Type | Required | Description |
|---|---|---|---|
| title | string | No | Chat title. Auto-set from first message if omitted. |
Response 201 Created
{
"id": "chat_a1b2c3d4e5f6",
"configId": "cfg_a1b2c3d4e5f67890",
"userId": "user_abc123",
"title": "Help with billing",
"messages": []
}
Errors
| Status | Error | Cause |
|---|---|---|
| 401 | Authentication required | Missing or invalid token |
/chats
List the authenticated user's chats, ordered by last updated.
Request
curl https://agent.smoothcodes.com/chats \
-H "Authorization: Bearer USER_TOKEN"
Response 200 OK
{
"chats": [
{
"id": "chat_a1b2c3d4e5f6",
"configId": "cfg_a1b2c3d4e5f67890",
"userId": "user_abc123",
"title": "Help with billing",
"createdAt": "2025-01-15T10:30:00.000Z",
"updatedAt": "2025-01-15T12:00:00.000Z"
}
]
}
/chats/:id
Get a chat with its full message history.
Request
curl https://agent.smoothcodes.com/chats/chat_a1b2c3d4e5f6 \
-H "Authorization: Bearer USER_TOKEN"
Response 200 OK
{
"id": "chat_a1b2c3d4e5f6",
"configId": "cfg_a1b2c3d4e5f67890",
"userId": "user_abc123",
"title": "Help with billing",
"messages": [
{ "role": "user", "content": "How do I upgrade my plan?" },
{ "role": "assistant", "content": "You can upgrade...", "model": "claude-sonnet-4-20250514" }
],
"createdAt": "2025-01-15T10:30:00.000Z",
"updatedAt": "2025-01-15T12:00:00.000Z"
}
Errors
| Status | Error | Cause |
|---|---|---|
| 401 | Authentication required | Missing or invalid token |
| 404 | Chat not found | Chat does not exist or is not accessible by this user |
/chats/:id/prompt
Send a message to the agent and receive a streaming SSE response. The response includes X-Prompt-Id, X-Chat-Id, and X-Request-Id headers.
Request
curl -N -X POST https://agent.smoothcodes.com/chats/chat_a1b2c3d4e5f6/prompt \
-H "Content-Type: application/json" \
-H "Authorization: Bearer USER_TOKEN" \
-d '{
"content": "How do I upgrade my plan?",
"files": [
{
"type": "image/png",
"data": "iVBORw0KGgo...",
"name": "screenshot.png"
}
],
"config": {
"llmModel": "claude-haiku-4-5-20251001",
"temperature": "0.5"
}
}'
| Field | Type | Required | Description |
|---|---|---|---|
| content | string | Yes | The user's message text |
| files | array | No | File attachments: [{type, data (base64), name}] |
| config | object | No | Per-prompt config overrides (model, temperature, etc.) |
Response: SSE Stream
Content-Type: text/event-stream
data: {"type":"text-delta","delta":"You can "}
data: {"type":"text-delta","delta":"upgrade your "}
data: {"type":"text-delta","delta":"plan by..."}
data: {"type":"tool-call","toolCallId":"tc_1","toolName":"getBillingInfo","args":{}}
data: {"type":"tool-result","toolCallId":"tc_1","result":"Current plan: Free"}
data: {"type":"text-delta","delta":"Based on your current plan..."}
data: {"type":"finish","finishReason":"stop"}
data: [DONE]
Response Headers
| Header | Description |
|---|---|
| X-Prompt-Id | Unique ID for this prompt (prm_xxx) |
| X-Chat-Id | The chat ID |
| X-Request-Id | Unique request ID (req_xxx) |
Errors
| Status | Error | Cause |
|---|---|---|
| 400 | content required | Message content is empty |
| 400 | No API key configured | Agent has no LLM API key set |
| 404 | Chat not found | Chat does not exist or is not accessible |
| 429 | Concurrent limit reached | Too many concurrent requests for this agent's plan |
/chats/:id/prompt
Cancel a running prompt for a chat.
Request
curl -X DELETE https://agent.smoothcodes.com/chats/chat_a1b2c3d4e5f6/prompt \
-H "Authorization: Bearer USER_TOKEN"
Response 200 OK
{ "cancelled": 1 }
/chats/:id/prompts
List prompt history for a chat (last 50 prompts).
Request
curl https://agent.smoothcodes.com/chats/chat_a1b2c3d4e5f6/prompts \
-H "Authorization: Bearer USER_TOKEN"
Response 200 OK
{
"prompts": [
{
"id": "prm_a1b2c3d4e5f6",
"content": "How do I upgrade?",
"status": "done",
"response": "You can upgrade by...",
"createdAt": "2025-01-15T12:00:00.000Z",
"updatedAt": "2025-01-15T12:00:05.000Z"
}
]
}
/chats/:id
Delete (hide) a chat. Also cleans up any VPS session and R2 files in the background.
Request
curl -X DELETE https://agent.smoothcodes.com/chats/chat_a1b2c3d4e5f6 \
-H "Authorization: Bearer USER_TOKEN"
Response 200 OK
{ "ok": true }
Chat Stateless
For one-off requests without creating a chat or managing history. No authentication required (but you must provide a config inline or the worker must have default env vars).
/chat
Send messages with inline config. No auth needed. Useful for quick integrations and testing.
Request
curl -N -X POST https://agent.smoothcodes.com/chat \
-H "Content-Type: application/json" \
-d '{
"messages": [
{ "role": "user", "content": "What is the capital of France?" }
],
"config": {
"llmProvider": "anthropic",
"llmApiKey": "sk-ant-api03-your-key",
"llmModel": "claude-haiku-4-5-20251001",
"systemPrompt": "Be concise.",
"maxSteps": 5
}
}'
| Field | Type | Required | Description |
|---|---|---|---|
| messages | array | Yes | Array of {role, content} messages |
| config | object | No | Inline agent config (provider, key, model, etc.) |
Response: SSE Stream
data: {"type":"text-delta","delta":"The capital"}
data: {"type":"text-delta","delta":" of France is Paris."}
data: {"type":"finish","finishReason":"stop"}
data: [DONE]
/chat/mock
Returns a mock SSE stream for testing your parser without using any LLM credits.
Request
curl -N -X POST https://agent.smoothcodes.com/chat/mock
Response: SSE Stream
data: {"type":"text-delta","delta":"This is "}
data: {"type":"text-delta","delta":"a mock response "}
data: {"type":"text-delta","delta":"for testing."}
data: {"type":"finish","finishReason":"stop"}
data: [DONE]
Agent Config Fields
These fields can be set when creating or updating an agent via POST /dashboard/configs or PUT /dashboard/configs/:id.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
mcpUrl | string | No | null | MCP server URL (legacy, prefer mcpServers) |
mcpToken | string | No | null | Authorization header for the MCP server |
mcpServers | array | No | [] | Multi-MCP: [{name, url, token?}] |
llmProvider | string | No | auto-detect | anthropic, openai, google, or auto-detected from key prefix |
llmApiKey | string | No | null | LLM provider API key (stored AES-GCM encrypted) |
llmModel | string | No | provider default | Model ID (e.g. claude-sonnet-4-20250514, gpt-4o) |
llmBaseUrl | string | No | null | Custom base URL for OpenAI-compatible APIs (e.g. Groq, Together) |
themeName | string | No | "Chat" | Display name shown in the chat UI |
themeColor | string | No | "#6366f1" | Accent color (hex) |
themeLogo | string | No | null | Logo URL for the chat widget |
systemPrompt | string | No | null | System prompt sent with every request |
maxHistory | number|null | No | null | null=unlimited, 0=stateless (only last message), N=sliding window of N messages |
maxTokens | number|null | No | null | Maximum output tokens per response |
temperature | number|null | No | null | Sampling temperature (0.0 to 2.0). Lower = more deterministic. |
maxSteps | number|null | No | 15 | Maximum agentic loop iterations (tool call rounds) |
maxBudgetUsd | number|null | No | null | Maximum budget per session in USD |
webSearch | boolean | No | false | Enable built-in web search tool |
webhookUrl | string|null | No | null | Pre/post tool execution hooks. Receives JSON with event, toolName, args/result. |
allowedOrigins | string | No | null | Comma-separated allowed CORS origins |
Multi-MCP Example
{
"mcpServers": [
{ "name": "crm", "url": "https://crm-mcp.example.com/mcp", "token": "Bearer crm-token" },
{ "name": "billing", "url": "https://billing-mcp.example.com/mcp" },
{ "name": "internal", "url": "internal" }
]
}
Tools from all MCP servers are merged and available to the agent. Use "url": "internal" to include SmoothAgent's built-in admin tools.
Webhook Hooks
When webhookUrl is set, the agent sends HTTP POST requests before and after each tool call:
Pre-hook (before tool execution):
{
"event": "tool.before",
"toolName": "searchDatabase",
"toolCallId": "tc_123",
"args": { "query": "user billing" }
}
Return {"allow": false, "reason": "Blocked"} to prevent tool execution.
Post-hook (after tool execution, fire-and-forget):
{
"event": "tool.after",
"toolName": "searchDatabase",
"toolCallId": "tc_123",
"result": { "rows": [...] }
}
Stream Format (SSE)
All streaming endpoints return text/event-stream with JSON payloads. Each line is prefixed with data: .
Standard Event Types
| type | Fields | Description |
|---|---|---|
text-delta | { delta } | Partial text token from the model |
thinking-delta | { delta } | Extended thinking/reasoning token |
reasoning-delta | { delta } | Alias for thinking-delta (some providers) |
tool-call | { toolCallId, toolName, args? } | Agent is calling a tool |
tool-input-start | { toolCallId, toolName } | Alias for tool-call (Vercel AI SDK format) |
tool-result | { toolCallId, result } | Tool returned a result |
tool-output-available | { toolCallId, result } | Alias for tool-result (Vercel AI SDK format) |
tool-output-error | { toolCallId, errorText } | Tool call failed |
error | { errorText } | Stream-level error |
finish | { finishReason } | Stream completed: stop, length, or error |
The stream always ends with:
data: [DONE]
SSE Parsing Example (Browser)
async function streamPrompt(chatId, content, token) {
const res = await fetch('https://agent.smoothcodes.com/chats/' + chatId + '/prompt', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token,
},
body: JSON.stringify({ content }),
});
const promptId = res.headers.get('X-Prompt-Id');
const reader = res.body.pipeThrough(new TextDecoderStream()).getReader();
let buffer = '';
let fullText = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += value;
const lines = buffer.split('\n');
buffer = lines.pop(); // keep incomplete line in buffer
for (const line of lines) {
if (!line.startsWith('data: ')) continue;
const payload = line.slice(6);
if (payload === '[DONE]') break;
const event = JSON.parse(payload);
switch (event.type) {
case 'text-delta':
fullText += event.delta;
document.getElementById('output').textContent = fullText;
break;
case 'tool-call':
console.log('Tool called:', event.toolName);
break;
case 'tool-result':
console.log('Tool result:', event.result);
break;
case 'error':
console.error('Error:', event.errorText);
break;
case 'finish':
console.log('Finished:', event.finishReason);
break;
}
}
}
return { text: fullText, promptId };
}
Claude Code Path (Additional Events)
When the agent uses a Claude Code provider (sk-ant-oat01-* key), additional event types appear:
| type | Fields | Description |
|---|---|---|
assistant | { message } | Accumulated message with content blocks |
tool_use | { name, input } | Standalone tool call (name and input) |
tool_result | { content } | Standalone tool result |
result | { session_id, usage, cost } | Final event with session_id, usage stats, and cost |
session | { session_id } | Session ID for resuming conversation |
The Worker translates Claude Code events into the standard format (text-delta, tool-call, etc.), so the SDK and parser work seamlessly with both paths.
Error Codes
All errors return JSON: { "error": "message" }
| Status | Meaning | Common Causes | Solution |
|---|---|---|---|
400 |
Bad Request | Missing required fields, invalid JSON, invalid plan name, nothing to update | Check your request body matches the endpoint's requirements |
401 |
Unauthorized | Missing Authorization header, expired token, invalid JWT signature | Include a valid Bearer token. Re-login if expired. |
403 |
Forbidden | Origin not allowed, email not verified, admin token required for endpoint | Check CORS settings, verify email, or use correct token type |
404 |
Not Found | Resource does not exist or not owned by authenticated user | Verify the ID is correct and belongs to the authenticated account |
409 |
Conflict | Email already registered (signup) | Use login instead, or use a different email |
413 |
Payload Too Large | Request body exceeds 20MB | Reduce file sizes or split into multiple requests |
429 |
Too Many Requests | Rate limit exceeded (30 req/min per IP), concurrent limit reached, login lockout | Wait and retry with backoff. Upgrade plan for higher limits. |
500 |
Internal Server Error | Unexpected server error, database error, upstream LLM failure | Retry the request. If persistent, check the agent's config. |
502 |
Bad Gateway | MCP server unreachable, VPS down, upstream provider timeout | Check MCP server health via /dashboard/mcp-health |
Rate Limits
Request Rate Limits
| Context | Limit | Window |
|---|---|---|
| Unauthenticated requests | 30 req/min per IP | 1 minute (sliding) |
| Authenticated requests | No rate limit | N/A (concurrency limits apply) |
| Login attempts (per email) | 5 attempts | 15 min lockout after 5 failures |
Concurrency Limits (per agent)
| Plan | Included | Hard Cap | Overage |
|---|---|---|---|
| Free | 1 | 1 | Blocked (upgrade to Pro) |
| Pro | 10 | 100 | Metered via Stripe |
| Business | 100 | 1,000 | Metered via Stripe |
| Enterprise | 1,000 | 10,000 | Metered via Stripe |
Rate Limit Headers
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests allowed in the current window |
X-RateLimit-Remaining | Requests remaining in the current window |
X-RateLimit-Reset | Unix timestamp when the window resets |
Retry Example
async function fetchWithRetry(url, opts, maxRetries = 3) {
for (let i = 0; i <= maxRetries; i++) {
const res = await fetch(url, opts);
// Don't retry client errors (4xx) except 429
if (res.status >= 400 && res.status < 500 && res.status !== 429) {
return res;
}
if (res.ok) return res;
if (res.status === 429) {
// Use the Retry-After header if provided, otherwise exponential backoff
const retryAfter = res.headers.get('Retry-After');
const waitMs = retryAfter
? parseInt(retryAfter) * 1000
: 1000 * Math.pow(2, i);
await new Promise(r => setTimeout(r, waitMs));
continue;
}
// 5xx: retry with exponential backoff
if (i < maxRetries) {
await new Promise(r => setTimeout(r, 1000 * Math.pow(2, i)));
continue;
}
return res;
}
}
Providers
SmoothAgent auto-detects the provider from the API key prefix, or you can set llmProvider explicitly.
| Provider | Key Prefix | Default Model | Notes |
|---|---|---|---|
| Anthropic | sk-ant-api* | claude-sonnet-4-20250514 | Claude models, extended thinking support |
| OpenAI | sk- | gpt-4o | GPT models, function calling |
AIza* | gemini-2.0-flash | Gemini models | |
| Claude Code | sk-ant-oat01-* | Agent mode | Persistent sessions, real tool execution, workspace |
| Custom | any | per config | Set llmBaseUrl for OpenAI-compatible APIs |
Custom Provider Example (Groq)
curl -X POST https://agent.smoothcodes.com/dashboard/configs \
-H "Content-Type: application/json" \
-H "Authorization: Bearer DEV_TOKEN" \
-d '{
"llmProvider": "openai",
"llmApiKey": "gsk_your_groq_key",
"llmModel": "llama-3.3-70b-versatile",
"llmBaseUrl": "https://api.groq.com/openai/v1",
"themeName": "Groq Agent"
}'
Any provider with an OpenAI-compatible chat completions API works with llmBaseUrl. Examples: Groq, Together, Fireworks, Ollama, LM Studio.
Claude Code Agent Mode
When using an OAuth subscription token (sk-ant-oat01-*), requests are routed to a VPS running ClaudeAPI in Docker containers:
- Persistent sessions: First message creates a session (saved to D1). Subsequent messages reuse it.
- Real tool execution: Claude Code can execute code, read/write files, and use tools in a sandboxed environment.
- Workspace: Uploaded files (via R2) are synced to the VPS workspace.
- Provider switching: Switching from another provider to Claude Code injects the full conversation history as CLAUDE.md context.
- Session recovery: Expired sessions (24h TTL) are automatically re-created with history bridging.
- Docker isolation: Each session runs in a container with network disabled, read-only filesystem, and 512MB memory limit.
Integration Examples
Node.js Full Example
import { SmoothAgent } from 'smoothagent';
// 1. Admin creates an agent and generates a user token
const admin = new SmoothAgent({
token: process.env.DEV_TOKEN,
workerUrl: 'https://agent.smoothcodes.com',
});
// Create an agent
const { id: configId } = await admin.createAgent({
llmProvider: 'anthropic',
llmApiKey: process.env.ANTHROPIC_API_KEY,
systemPrompt: 'You are a customer support agent for Acme Inc.',
themeName: 'Acme Support',
});
// Generate a user token for the end-user
const { token: userToken } = await admin.createUser('user_jane', { configId });
// 2. End-user uses their token to chat
const user = new SmoothAgent({
token: userToken,
workerUrl: 'https://agent.smoothcodes.com',
});
const chat = await user.createChat('Help with my order');
const result = await user.prompt(chat.id, 'Where is my order #12345?', {
onDelta: (delta) => process.stdout.write(delta),
onToolCall: ({ toolName }) => console.log('\n[Tool]', toolName),
onToolResult: ({ result }) => console.log('[Result]', result),
onFinish: ({ text, reason }) => console.log('\n[Done]', reason),
});
console.log('\nFull response:', result.text);
console.log('Usage:', result.usage);
Browser ES Module
<script type="module">
import { SmoothAgent } from 'https://agent.smoothcodes.com/js/sdk.js';
const agent = new SmoothAgent({ token: 'USER_TOKEN' });
const chat = await agent.createChat();
for await (const event of agent.stream(chat.id, 'Hello!')) {
if (event.type === 'text-delta') {
document.body.textContent += event.delta;
}
}
</script>
React Component
import { useState, useCallback } from 'react';
import { SmoothAgent } from 'smoothagent';
const agent = new SmoothAgent({ token: 'USER_TOKEN' });
function Chat() {
const [messages, setMessages] = useState([]);
const [input, setInput] = useState('');
const [chatId, setChatId] = useState(null);
const [streaming, setStreaming] = useState(false);
const send = useCallback(async () => {
if (!input.trim() || streaming) return;
let currentChatId = chatId;
if (!currentChatId) {
const chat = await agent.createChat();
currentChatId = chat.id;
setChatId(currentChatId);
}
setMessages(prev => [...prev, { role: 'user', content: input }]);
setMessages(prev => [...prev, { role: 'assistant', content: '' }]);
setInput('');
setStreaming(true);
await agent.prompt(currentChatId, input, {
onDelta: (delta) => {
setMessages(prev => {
const updated = [...prev];
const last = updated[updated.length - 1];
updated[updated.length - 1] = { ...last, content: last.content + delta };
return updated;
});
},
onFinish: () => setStreaming(false),
onError: (err) => {
console.error(err);
setStreaming(false);
},
});
}, [input, chatId, streaming]);
return (
<div>
{messages.map((m, i) => (
<div key={i} className={m.role}>
<strong>{m.role}:</strong> {m.content}
</div>
))}
<input
value={input}
onChange={e => setInput(e.target.value)}
onKeyDown={e => e.key === 'Enter' && send()}
placeholder="Type a message..."
/>
<button onClick={send} disabled={streaming}>Send</button>
</div>
);
}
cURL Step-by-Step
# 1. Login
TOKEN=$(curl -s -X POST https://agent.smoothcodes.com/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"you@example.com","password":"pass123"}' \
| jq -r '.token')
# 2. List your agents
curl -s https://agent.smoothcodes.com/dashboard/configs \
-H "Authorization: Bearer $TOKEN" | jq '.configs[].id'
# 3. Generate a user token for an agent
USER_TOKEN=$(curl -s -X POST https://agent.smoothcodes.com/dashboard/configs/CONFIG_ID/token \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"userId":"user_test"}' \
| jq -r '.token')
# 4. Create a chat
CHAT_ID=$(curl -s -X POST https://agent.smoothcodes.com/chats \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $USER_TOKEN" \
-d '{"title":"Test chat"}' \
| jq -r '.id')
# 5. Send a prompt (stream to terminal)
curl -N -X POST https://agent.smoothcodes.com/chats/$CHAT_ID/prompt \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $USER_TOKEN" \
-d '{"content":"Hello! What can you help me with?"}'