Ship your agent into anything.
One deployed Something agent gives you chat, persistent conversations, isolated customer data, real-time voice, and a typed MCP tool surface.
Build a product
Call the agent from your backend with JSON or stream responses over SSE.
Connect AI tools
Expose the same agent to Codex and Claude Code through one MCP endpoint.
Keep users isolated
A stable user id partitions conversations and datastore rows automatically.
Your first agent call in five minutes
Deploy an agent, mint an agent-scoped key, and send a backend request. You do not need an SDK to get started.
Deploy an agent
Build and publish from the Something workspace.
Open workspace2Create a secret
Choose that agent under Developer settings.
Create a key3Call chat
Keep the key server-side and send a Bearer header.
View endpointconst response = await fetch(
"https://something.cloud/api/v1/agents/AGENT_ID/chat",
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SOMETHING_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
message: "Summarize today’s customer feedback.",
user: "customer_123",
}),
},
);
const { message, conversation_id } = await response.json();user value should be an opaque id from your own auth system—not an email or display name. Reuse it on every call for the same customer.Authenticate every request
API keys are shown once, stored as hashes, and scoped to one deployed agent. They spend the owner’s usage credits.
Server-side only
Store the key in a secret manager or environment variable. Never place it in browser JavaScript, a mobile binary, logs, or source control.
One key, one agent
A key cannot call a different agent id. Use separate keys for local development, staging, production, and each teammate.
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxx/agents/agents/{agent_id}Connect Something to your coding agent
The Streamable HTTP endpoint discovers the agent attached to your Bearer key. No agent id is needed in individual MCP tool calls.
https://something.cloud/api/mcp[mcp_servers.something_agent]
url = "https://something.cloud/api/mcp"
bearer_token_env_var = "SOMETHING_API_KEY"
enabled = trueclaude mcp add --transport http \
--scope user something-agent \
https://something.cloud/api/mcp \
--header "Authorization: Bearer $SOMETHING_API_KEY"Tools exposed automatically
get_agentInspect agent identity and capabilities
chat_with_agentRun a chat turn and continue threads
list_agent_conversationsList one user’s recent threads
get_conversation_messagesRead a conversation transcript
list_agent_dataQuery rows from a named table
create_agent_dataInsert structured JSON data
Inspect the connected agent
Use the introspection endpoints to validate a key and discover whether its agent supports voice.
/agents/agents/{agent_id}{
"id": "7d1660ce5cfb",
"name": "Customer research copilot",
"description": "Synthesizes interviews and feedback",
"capabilities": {
"chat": true,
"voice": false,
"data": true
}
}Chat and stream responses
Send a message, optionally continue an existing conversation, and choose one JSON response or token-level Server-Sent Events.
/agents/{agent_id}/chatRequest body
messageRequireduserconversation_idstreamattachments{
"message": "The strongest theme is faster onboarding.",
"conversation_id": "5f3c9e4a-…",
"usage": { "input_tokens": 812, "output_tokens": 47 },
"credits": { "credits": 3, "balance_after": 49872 }
}Streaming events
Set stream: true and accept text/event-stream. The conversation id arrives first, followed by text, usage and credits, then done.
event: conversation
data: {"conversation_id":"5f3c9e4a-…"}
event: text
data: {"text":"The strongest theme"}
event: credits
data: {"credits":3,"balance_after":49872}
event: done
data: {"stop_reason":"end_turn"}Manage conversation history
Every message belongs to a thread. The same user value used for chat must be supplied when reading, renaming, or deleting that user’s conversation.
/agents/{agent_id}/conversations?user={user}&limit=100/agents/{agent_id}/conversations/agents/{agent_id}/conversations/{id}/messages?user={user}/agents/{agent_id}/conversations/{id}/agents/{agent_id}/conversations/{id}?user={user}curl https://something.cloud/api/v1/agents/AGENT_ID/conversations \
-H "Authorization: Bearer $SOMETHING_API_KEY" \
-H "Content-Type: application/json" \
-d '{"user":"customer_123","title":"Q3 feedback review"}'Use the agent data store
Each agent includes a schemaless, per-user JSON store—the same persistence surface used by generated agent interfaces.
/agents/{agent_id}/data/{table}?user={user}&limit=50&order=desc/agents/{agent_id}/data/{table}/_count?user={user}/agents/{agent_id}/data/{table}/{row_id}?user={user}/agents/{agent_id}/data/{table}/agents/{agent_id}/data/{table}/{row_id}/agents/{agent_id}/data/{table}/{row_id}?user={user}{
"user": "customer_123",
"data": {
"title": "Review onboarding",
"status": "open"
}
}{
"user": "customer_123",
"patch": {
"status": "done"
}
}Start a voice session
Voice agents return LiveKit connection details that work with the web, iOS, Android, React Native, Flutter, and other LiveKit client SDKs.
/agents/{agent_id}/voice/sessionuserlanguageconversation_id{
"room_name": "a-3f2c…",
"livekit_url": "wss://voice.trysomething.sh",
"client_token": "eyJhbGci…"
}Request a session from your backend
Pass URL + token to Room.connect()
Publish the microphone track
Keep every customer isolated
Something never accepts a raw internal end-user key. Your API key id and user subject are combined server-side into a tenant boundary.
Embedding with your own JWT
For the hosted agent interface, configure End users → Authentication, mint a short-lived JWT on your server, and pass it as eu_token. The token is captured and removed from the URL on load.
const token = jwt.sign(
{ sub: user.id, email: user.email, name: user.name },
process.env.AGENT_SIGNING_SECRET,
{ algorithm: "HS256", expiresIn: "1h" },
);Let your customers sign in with Google
Your app gets a “Continue with Google” button backed by your own Google Cloud OAuth client — one per environment. Your client secret stays on our servers and is never shipped to the browser.
1. Create the OAuth client
In Google Cloud Console → APIs & Services: configure the OAuth consent screen, then Credentials → Create credentials → OAuth client ID → Web application. Leave Authorized JavaScript origins empty — sign-in runs server-side and never calls Google from the browser.
2. Register one redirect URI per environment
Google matches redirect URIs exactly, character for character. Add these under Authorized redirect URIs — the same three however many domains your app is served on, because sign-in always returns through the platform and is then handed back to your app.
production https://something.cloud/api/end-user-auth/google/callback
staging https://staging.trysomething.sh/api/end-user-auth/google/callback
development http://localhost:3000/api/end-user-auth/google/callbackUse a separate client ID and secret per environment. Rotating a staging secret then can’t take production down, and a production credential can never be used from a staging host — the environment is chosen by the callback URL the customer actually arrived on, not by anything the browser sends.
3. Paste the pair into your app
Configure ▸ End users ▸ Google, one card per environment. The client secret is encrypted at rest, is never returned by any read, and never reaches your generated frontend — there is no client-side field for it, by design. Saving a client also switches the app to app accounts, which is what issues the session.
Scopes stay at openid email profile unless you have a reason: every extra scope appears on Google’s consent screen and costs you sign-ups.
4. The button appears by itself
<SignInForm /> renders “Continue with Google” whenever a client is connected for the environment it’s viewed in, and renders nothing extra when there isn’t — so an app set up for production doesn’t show a dead button on staging. Hand-rolling a sign-in screen instead? Use the same hook.
const auth = useEndUserAuth();
{auth.googleEnabled && (
<GoogleSignInButton onDone={() => setShowAuth(false)} />
)}
{auth.error && <FlowStateCard tone="error" title={auth.error} />}Google sign-in uses the same accounts as email and password. Someone who signed up with a password and later presses the Google button lands on the same account — linking is by the email address Google has verified — so their history, subscription and data follow them.
Troubleshooting
google_not_configuredNo Google client is saved for the environment the customer is in. Add one in Configure ▸ End users ▸ Google.google_redirect_mismatchThe app was opened on a host with no matching client, or the URI in Google Cloud Console differs by a character. Copy the exact URI from the panel — scheme and path included.google_invalid_clientGoogle rejected the id/secret pair. Re-copy both; a secret rotated in Google Cloud Console stops working here immediately.google_credentials_revokedGoogle reports the credentials as revoked or expired. Create a new client secret and paste it in.google_consent_deniedThe customer dismissed Google’s consent screen, or an admin policy blocked it. Nothing to fix.google_email_unverifiedThe customer’s Google account has an unverified email. Accounts are keyed by email, so we won’t link an address Google won’t vouch for.google_state_invalidThe attempt took over ten minutes, or cookies for the platform domain are blocked. Retrying usually fixes it.google_unavailableGoogle was unreachable. Transient — retry.Customers see a plain sentence, never a code. The codes above appear in your logs and in Check setup on each environment card, which runs the same resolution a live sign-in does and tells you which half is missing.
Errors are structured and actionable
REST endpoints use standard HTTP status codes. FastAPI validation failures may use a detail object; API-level failures expose an error code and message.
unauthorizedThe Bearer key is missing, invalid, or revoked.insufficient_creditsThe agent owner needs more use credits.forbiddenThe key is not scoped to the requested agent.not_foundThe agent, conversation, or datastore row does not exist.validation_errorA path, query, or request body field is invalid.rate_limitedThe per-key request window has been exceeded.agent_errorThe agent failed to complete the requested turn.{
"error": "rate_limited",
"message": "Rate limit of 120 requests/min exceeded.",
"retry_after_seconds": 18
}Rate limits and retries
Each API key has its own request window. The default is 120 requests per minute and can be adjusted for larger workloads.
Retry only requests that are safe for your application. Respect the Retry-After header and add randomized jitter to prevent synchronized retries.
Credits and billing
API traffic uses the same owner-funded use-credit pool as traffic through the hosted agent interface.
Chat
Metered by model tokens. The response includes usage and credit fields.
Voice
Metered by connected call time for supported voice agents.
Data + history
No model call, so reads and writes do not spend model credits.
Production checklist
A short review before your integration handles real customer traffic.
Build the agent. Connect the surface.
Create an agent-scoped credential and get a ready-to-paste setup prompt for Codex, Claude Code, or your backend.
Open Developer settings