Start Here
Quickstart
An agent that runs in camelRun, calls a tool in your own process, and answers you
View as Markdown · Docs index for coding agents
Set up with your coding agent. Paste this into Claude Code, Codex, Cursor or any coding agent. It asks you what the agent should do, installs the SDK, connects your account without pasting keys into chat, and runs your first agent.
Read https://run.camelai.com/SKILL.md and set up camelRun in this project. Ask me what the agent should do.In five minutes you will have an agent that runs in camelRun, calls a tool in your own process, and answers you. camelRun keeps the model loop, the agent's history and its files. Your tools stay in your code, with your credentials.
Credit
Signing in to the console with GitHub or Google creates your account. GitHub accounts older than 30 days start with free credit. Everyone else verifies a card on the Billing page to unlock starting credit: the card is saved, not charged. Runs use credit even when you bring your own provider key.
Try it from your coding agent (no key)
Connect the hosted MCP server and sign in. There is no key to create or copy.
Add the camelRun MCP server
claude mcp add --transport http camelrun https://run.camelai.com/mcp
# then run /mcp in Claude Code and sign in with GitHub or GoogleCursor, VS Code and Claude: see Connect your coding agent.
Ask for an agent
Use the camelrun MCP server to create my first camelRun agent: ask me what it should do, create it, run it once, and show me its reply.Your agent appears under Agents in the console.
Build it into your app
Get an API key
Sign in to the console. Under API tokens, create a token and export it:
export CAMELAI_API_KEY=art_...The key can create and control every agent in your account. Keep it on your server and never ship it to a browser.
Install the SDK
# Node 22 or later, or Bun
npm install @camelai/runRun an agent
import { Agents, schema, tool } from "@camelai/run";
const agents = new Agents(); // reads CAMELAI_API_KEY
// A tool is an ordinary function. It runs here, in your process.
const weather = tool({
description: "Today's weather in a city",
input: schema.Object({ city: schema.String() }),
execute: ({ city }) => ({ city, forecast: "sunny", highC: 24 }),
});
// The same key is the same agent, with its history, every time you run this.
const agent = await agents.upsert("quickstart", {
instructions: "You are a concise assistant.",
tools: { weather },
});
const run = await agent.run("Should I bring an umbrella in Lisbon today?");
console.log(run.text);
await agents.close();Run it with npx tsx quickstart.mts or python quickstart.py.
.mts makes the TypeScript an ES module, which top-level await needs; in
a project with "type": "module", .ts works too. The Python SDK is
async-only: call it from async code, and from synchronous code (a script,
a Django view, a Celery task) with asyncio.run(...).
The examples name no model, so the agent gets your account's default: a model your account can use.
Choose a model (optional)
List the models your account can use, then name one:
npx -y @camelai/camelrun models --availableconst agent = await agents.upsert("quickstart", {
model: "<a model from the list>", // e.g. provider/model-id
instructions: "You are a concise assistant.",
tools: { weather },
});If a run fails with code model_key_missing, the agent names a model your
account has no key for, and the error says which key to set: pick a model from
the list, leave model out, or add the provider's key under Models & keys.
With curl
Without an SDK, an agent can't call tools in your process, but everything else works over REST. Create an agent with a first prompt, then poll the run:
BASE=https://run.camelai.com; AUTH="Authorization: Bearer $CAMELAI_API_KEY"
# The Idempotency-Key is the agent's key: the same key is the same agent.
CREATED=$(curl -s $BASE/v1/agents -H "$AUTH" -H "Content-Type: application/json" \
-H "Idempotency-Key: quickstart-curl" \
-d '{"systemPrompt": "You are a concise assistant.",
"prompt": {"text": "Write a haiku about durable agents."}}')
AGENT=$(echo "$CREATED" | jq -r .id); REQ=$(echo "$CREATED" | jq -r .prompt.id)
# ?wait=25 answers as soon as the run ends (or after 25 s, still running: ask again).
until RUN=$(curl -s "$BASE/v1/agents/$AGENT/requests/$REQ?wait=25" -H "$AUTH") && jq -e '.state == "completed"' <<< "$RUN" > /dev/null; do :; done
jq -r 'if .status == "failed" then "failed: \(.error)" else .outcome.result.reply end' <<< "$RUN"Send later messages with POST /v1/agents/{id}/prompt {"text": "..."}, which
answers 202 with the request to poll. See REST API.
Stream the reply
run() waits for the whole run. To show it as it happens, stream() yields
text as the model writes it, each tool call and result, and the finished run
last:
for await (const part of agent.stream("And tomorrow?")) {
if (part.type === "text") process.stdout.write(part.text);
if (part.type === "tool_call") console.log(`\n[${part.name}(${JSON.stringify(part.arguments)})]`);
if (part.type === "done") console.log(`\n(${part.run.status})`);
}The run's result is the source of truth, and the stream is for display. If a
stream breaks, the run isn't lost: run() (or stream.result()) still
resolves with it.
What just happened
upsert("quickstart", ...)made a keyed agent, or found the one from last time and brought it to this configuration. It lives until you delete it, so running the file again continues the same conversation.- Because the agent has
tools, this process served them. camelRun calledweatherhere, over the connection the SDK holds. One process at a time serves an agent's tools, and only while it runs. Serverless functions, several web workers or task workers, and multi-user backends serve tools over HTTP instead, which also keeps them working through deploys; see Several processes, workers and deploys. run()resolved with aRun:status(completed,input_requiredorfailed),text,inputs,error,toolErrorsandtoolCalls(Pythontool_errors,tool_calls). A failed run throws aRunErrorunless you passthrowOnError: false(throw_on_error=False). Acompletedrun can still list tool errors, e.g.connection_lostwhen the process serving its tools went away mid-call: check them.agents.close()let the process exit. The agent stays in camelRun.