Guides
camelRun production checklist for AI agent apps
What to check before you ship an AI agent app on camelRun: keys and access, agents, tools, runs and operations.
View as Markdown · Docs index for coding agents
Before you ship an application on camelRun, check each of these.
Keys and access
- Your API key is only on your servers, in a secret store, never in a browser, a mobile app or a repository. Browsers get browser tokens, minted per user after your own access checks.
- An agent's token (
agent.session.token) is secret too. You rarely need it: upsert by key instead. The SDKs keep it out of logs and JSON; don't print it yourself. - Tools authorize every call as
context.identity.userwithinidentity.context, never from ids in the model's arguments. See Identity.subjectandcontextare set when the agent is made. - Served tools verify camelRun's token (
serveTools,serve_toolsandruntimeAuthdo) withtenantset to your own tenant, so another account's agents can't act as your users. - Behind a TLS-terminating proxy, pass
origintonodeListenerso tokens are checked against your public URL (ortrustProxy: true, only where the proxy overwritesX-Forwarded-*). Test withtestRuntime().
Agents
- Agents are keyed by your ids (
upsert("user-123", ...)), so any process finds them and a retry never makes a second one. Keyed agents live until deleted: delete the ones you no longer need (agent.delete()), for example when the user or thread is deleted. - Configuration lives in one place: the
upsertcall, or a definition withapply: "all"to roll changes out. Fields fixed at creation (subject,context,definition,mounts) are a 409 if they change. Handle that as "delete and recreate" only where you mean it; mounts change withremount: true. - You pick a model your account has a key for, and set
spendLimiton agents that act for untrusted users. To charge each user for what their agents spend, use end-user credits. - Agents are grouped in workspaces if you want usage per project or use case.
Tools
- Every tool with side effects passes
context.idempotencyKeyto what it calls, so a retried or re-run call acts once. - Long tools set
timeoutMsand reportcontext.progress(). Nothing relies on a call finishing within the default 15 seconds. - Tools that ask a person (
needsApproval,context.confirmorask) ask before they act, because everything before an ask runs again once answered. - Where tools run matches how you deploy. A long-lived process attaches them;
serverless or several instances serve them over HTTP. Agents woken by
schedules, channels or webhooks use served tools, or nobody may be there to
answer (
toolErrorswithnot_connected). A run with nobody serving attached tools is refused (APPLICATION_NOT_CONNECTED) unless it passesallowDisconnected. - Exactly one process attaches an agent's tools. Others run it with
attach: false, or without tools. A process that loses them (APPLICATION_REPLACEDononError) keeps running the agent without them.
Runs
- You reply to users from the run's result (
run.text), not from the event stream. The stream is for display. - Failures are handled:
run()throwsRunError(withcode, anduncertainwhen a restart cut the run short), andrun.toolErrorslists tool calls that didn't complete. - Runs that wait on people are shown to someone who can answer, or answered from
your inbox (
agents.runtime.inbox("pending")and theinput.requestedwebhook). An input no one answers in 15 minutes (the default) is answered by the runtime, never approved, and the run goes on. - Retries reuse the run's
idempotencyKey, so a request retried after a timeout finds the same run instead of starting another. - Waits have a bound where your caller needs one (
signal, Pythontimeout=), knowing the run goes on.agent.abort()stops it. onEventhandlers are quick or queue their work. They run in order, apart from the connection, and their errors go toonError.
Operations
- Your process closes its agents on shutdown, on SIGTERM (
await agents.close(), orawait using). It finishes the tool calls it is running, hands new ones to the next process and releases the agents' tools, so a deploy loses no call. A rolling deploy starts the new process withtakeover: true. See Several processes, workers and deploys. - You register a webhook endpoint for
run.failedandinput.requested(andusage.recordedto meter spend), verify signatures, dedupe by eventid, and answer within 10 seconds. - To trace runs in your own backend, you set trace export
(
PUT /v1/telemetry), passtraceparentfrom your request's span, send a test span, and decide deliberately whether to include content. - You handle 429 and 503 by waiting
Retry-After(the SDKs retry them), and know the limits your workload approaches. - Serverless: prefer served tools.