Agent API v1 Streamable HTTP MCP

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.

RESTJSON + SSE
MCP6 typed tools
VoiceLiveKit ready

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.

01 · Quickstart

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.

const 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();
Use a stable user id
The 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.
02 · Security

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.

HTTP header
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxx
GET/agents
List the agent attached to this key
GET/agents/{agent_id}
Inspect name and capabilities
Rotate, then revoke
If a secret may have leaked, create its replacement first, update every caller, verify traffic, and revoke the old key from Developer settings.
03 · MCP

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.

Streamable HTTP
https://something.cloud/api/mcp
Codex
~/.codex/config.toml
[mcp_servers.something_agent]
url = "https://something.cloud/api/mcp"
bearer_token_env_var = "SOMETHING_API_KEY"
enabled = true
Claude Code
terminal
claude mcp add --transport http \
  --scope user something-agent \
  https://something.cloud/api/mcp \
  --header "Authorization: Bearer $SOMETHING_API_KEY"

Tools exposed automatically

get_agent

Inspect agent identity and capabilities

chat_with_agent

Run a chat turn and continue threads

list_agent_conversations

List one user’s recent threads

get_conversation_messages

Read a conversation transcript

list_agent_data

Query rows from a named table

create_agent_data

Insert structured JSON data

Do not put secrets in URLs
Keep the key in an Authorization header or environment variable. URLs are retained in history, logs, monitoring systems, and intermediary proxies.
04 · REST

Inspect the connected agent

Use the introspection endpoints to validate a key and discover whether its agent supports voice.

GET/agents
Returns the one agent scoped to the key
GET/agents/{agent_id}
Returns metadata and capabilities
response
{
  "id": "7d1660ce5cfb",
  "name": "Customer research copilot",
  "description": "Synthesizes interviews and feedback",
  "capabilities": {
    "chat": true,
    "voice": false,
    "data": true
  }
}
05 · Core endpoint

Chat and stream responses

Send a message, optionally continue an existing conversation, and choose one JSON response or token-level Server-Sent Events.

POST/agents/{agent_id}/chat
Run one agent turn

Request body

messageRequired
stringThe end user’s message. Must not be empty.
user
stringYour stable end-user id. Omit for a single shared context.
conversation_id
stringContinue an existing thread. Omit to create or select one.
stream
booleanReturn SSE when true. Defaults to false.
attachments
arrayUp to 20 base64 attachments with kind, media_type, data, and optional name.
JSON response
{
  "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.

text/event-stream
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"}
06 · State

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.

GET/agents/{agent_id}/conversations?user={user}&limit=100
List recent threads
POST/agents/{agent_id}/conversations
Create a titled thread
GET/agents/{agent_id}/conversations/{id}/messages?user={user}
Read messages
PATCH/agents/{agent_id}/conversations/{id}
Rename a thread
DELETE/agents/{agent_id}/conversations/{id}?user={user}
Delete a thread
create conversation
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"}'
Conversation ownership is enforced
A thread created for one user cannot be read by passing a different user id. Something derives the internal tenant key on the server rather than trusting a raw end-user key.
07 · Persistence

Use the agent data store

Each agent includes a schemaless, per-user JSON store—the same persistence surface used by generated agent interfaces.

GET/agents/{agent_id}/data/{table}?user={user}&limit=50&order=desc
List table rows
GET/agents/{agent_id}/data/{table}/_count?user={user}
Count table rows
GET/agents/{agent_id}/data/{table}/{row_id}?user={user}
Read one row
POST/agents/{agent_id}/data/{table}
Create a row
PATCH/agents/{agent_id}/data/{table}/{row_id}
Patch a row
DELETE/agents/{agent_id}/data/{table}/{row_id}?user={user}
Delete a row
create row
{
  "user": "customer_123",
  "data": {
    "title": "Review onboarding",
    "status": "open"
  }
}
patch row
{
  "user": "customer_123",
  "patch": {
    "status": "done"
  }
}
Data and history calls do not run a model
Reading and writing the data store or conversation history does not spend model usage credits.
08 · Realtime

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.

POST/agents/{agent_id}/voice/session
Mint a client session
user
stringStable user id for history isolation.
language
stringOptional language override supported by the agent.
conversation_id
stringContinue an existing multimodal thread.
response
{
  "room_name": "a-3f2c…",
  "livekit_url": "wss://voice.trysomething.sh",
  "client_token": "eyJhbGci…"
}
01

Request a session from your backend

02

Pass URL + token to Room.connect()

03

Publish the microphone track

09 · Tenancy

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.

Your user id
API key id
Private tenant

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.

server-side JWT
const token = jwt.sign(
  { sub: user.id, email: user.email, name: user.name },
  process.env.AGENT_SIGNING_SECRET,
  { algorithm: "HS256", expiresIn: "1h" },
);
10 · Sign-in

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.

authorized redirect URIs
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/callback

Use 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.

hand-rolled sign-in
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.

11 · Reference

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.

401unauthorizedThe Bearer key is missing, invalid, or revoked.
402insufficient_creditsThe agent owner needs more use credits.
403forbiddenThe key is not scoped to the requested agent.
404not_foundThe agent, conversation, or datastore row does not exist.
422validation_errorA path, query, or request body field is invalid.
429rate_limitedThe per-key request window has been exceeded.
502agent_errorThe agent failed to complete the requested turn.
error response
{
  "error": "rate_limited",
  "message": "Rate limit of 120 requests/min exceeded.",
  "retry_after_seconds": 18
}
12 · Reliability

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.

120 rpmDefault per-key limit
Retry-AfterResponse header in seconds
BackoffRetry 429s with jitter

Retry only requests that are safe for your application. Respect the Retry-After header and add randomized jitter to prevent synchronized retries.

13 · Usage

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.

Insufficient balance returns 402
Chat and voice pause when the owner’s use-credit balance is empty. Top up under Billing, then retry the request.
View plans and credits
14 · Ship

Production checklist

A short review before your integration handles real customer traffic.

Use a separate agent-scoped key for production.
Store the key in a server-side secret manager.
Pass an opaque, stable user id on every customer-scoped call.
Persist conversation_id when your product needs durable threads.
Handle 401, 402, 429, and upstream 5xx responses explicitly.
Respect Retry-After and monitor credit balance from chat responses.
Rotate keys without downtime and revoke old credentials.

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
Something Agent API · REST v1 · Streamable HTTP MCPNeed help? Open the workspace or email support@trysomething.sh.
Docs · TrySomething