Call other tools from lambda tools
A lambda tool can call other tools — searches,
sub-agents, web fetches, even other lambdas — from its own Python code. It
declares tool_configurations (the same configuration shape agents use) and
calls each one as a plain function; each call runs server-side and returns its
result to the lambda. This lets a lambda reach data outside the sandbox — HTTP
APIs, corpora, artifacts — without itself getting network access.
This unlocks lambdas that:
- Call a guarded REST API through a pinned
web_getconfiguration, with the bearer token pulled from agent secrets — the credential never appears in your code, the sandbox, or session events. - Read files the user uploaded through the
artifact_readtool, then parse, validate, or reshape them with your own logic. - Search your corpora and post-process the hits before the agent sees them.
- Chain other lambdas, splitting a workflow into small, testable functions.
The composed tools stay private to the lambda: they never appear on the tool surface of an agent that uses the lambda, and their calls produce no session events. To the agent, your lambda is still one tool with one input and one output.
The tool module
When a lambda declares tool_configurations, its code gets a built-in tool
module — no import, no setup:
CALLING A CONFIGURED TOOL FROM LAMBDA CODE
Code example with python syntax.1
tool.<name>(param=value, ...)calls the configuration named<name>with keyword arguments and returns its result as a dict.tool.list()returns every callable tool's name, description, and input schema.- Failures raise
tool.ToolErrorwith the platform's error message — catch it if your lambda can degrade gracefully.
Run composed calls in parallel
When calls are independent, don't run them one at a time. Every callable also
carries tool.<name>.submit(param=value, ...): it starts the call and
returns a handle immediately; handle.result() waits for that call's result —
or re-raises that call's tool.ToolError, without affecting its siblings.
submit is a capability of the sandbox's tool module; the API reference for
tool_configurations describes only the synchronous surface
(tool.<name>(...), tool.list(), tool.ToolError).
PARALLEL FAN-OUT WITH PER-CALL ERROR ISOLATION
Code example with python syntax.1
Things to know:
- Submit everything before collecting anything. Calling
submitthen immediatelyresultin the same loop iteration runs the calls sequentially again — the pattern above starts all calls first, then collects. - A limited number of calls run concurrently — currently 8, a platform
behavior rather than an API guarantee. Submits beyond the limit queue and
start as slots free up — a wider fan-out just takes proportionally longer.
Concurrent executions of the same lambda in one session share a per-session
in-flight limit, and overflowing it raises
tool.ToolErrorfromhandle.result()— catch it per handle, as the example does. - Set
max_execution_time_secondsexplicitly — do not rely on a default. A lambda that declarestool_configurationsand omitsexecution_configurationentirely resolves to 300 seconds, whereas any other lambda resolves to 30. But supplyingexecution_configurationwithout this field stores 30, so most lambdas — including every one saved from the console — run at 30 unless you set it. Set it to the budget your fan-out actually needs. - Both timeout knobs accept up to 21600 seconds.
max_execution_time_secondsin the agent'stool_configurationsentry for the lambda takes precedence overexecution_configurationwhen both are set, so use it to give one agent a longer budget than the lambda's own default.
Example: parallel sub-agent fan-out
The pattern above is how you build a deep-research-style orchestrator: the model makes one call to a fan-out lambda, and the lambda parallelizes the delegation deterministically instead of depending on the model to emit many tool calls at once.
CREATE A RESEARCH FAN-OUT LAMBDA
Code example with curl syntax.1
The hosting agent then exposes it with a timeout sized for the fan-out:
AGENT TOOL CONFIGURATION
Code example with json syntax.1
Keep session_mode: ephemeral (the default) on a sub-agent configuration you
fan out to. persistent mode reuses one session per (parent session, tool
configuration) pair, so parallel submits through the same configuration are
funneled into a single session and return overlapping answers instead of one
answer per question. The same applies to session_scoped and agent_scoped
(and the deprecated llm_controlled alias) if you pass the same session_key
to concurrent submits — omit it and each call gets its own session.
Composed calls produce no session events, so the parent agent's clients see no sub-agent progress while the fan-out runs — results arrive when the lambda returns. Keep per-question scope tight enough that the whole fan-out fits the configured timeout.
Sub-agent chains are not depth-limited by the platform: a delegate that itself
fans out multiplies the work at every level. Keep the delegate graph acyclic
and size max_execution_time_seconds for the whole tree.
Variant: specialist branches
Instead of fanning one question list over a single sub-agent, give the lambda several specialist sub-agents and send each a differently-framed prompt. The hosting agent still makes one call; each branch fails independently and the survivors' answers come back regardless:
PARALLEL DEEP RESEARCH ACROSS SPECIALIST SUB-AGENTS
Code example with python syntax.1
Each specialist is its own tool_configurations entry — same shape as before,
one per branch, every one ephemeral:
ONE SUB_AGENT ENTRY PER SPECIALIST
Code example with json syntax.1
Example: a ticket summarizer with a secret-guarded API
This lambda wraps a ticketing API behind a single summarize_ticket tool. The
API needs a bearer token, but the token lives in the agent's secrets — the
lambda code only ever sees the response.
1. Create the lambda with its tool configuration
CREATE A COMPOSABLE LAMBDA TOOL
Code example with curl syntax.1
Everything in argument_override is decided by you, not by the lambda: the
code chooses only url_suffix, while the base URL comes from the hosting
agent's metadata and the token from its secrets, both resolved at execution
time.
2. Test it without an agent
POST /v2/tools/test runs lambda code before you create anything. The
test_context field stands in for the hosting agent and session, so $refs
resolve exactly as they will in production. For a test that runs longer than a
few minutes, set stream_response: true and accept text/event-stream
(budgets above 300 seconds require it): the
platform emits heartbeat events to keep the connection alive while the
function runs, and finishes with one result event carrying the same object
the non-streaming response returns.
TEST THE LAMBDA AND ITS TOOL CALLS
Code example with curl syntax.1
A $ref that names a key missing from test_context fails the test with a
resolution error — you find configuration mistakes here, not in production.
3. Attach the lambda to an agent
The agent references the lambda like any other tool. It sees one tool,
summarize_ticket, with one parameter:
AGENT TOOL CONFIGURATION
Code example with json syntax.1
At runtime the lambda's internal fetch_ticket resolves its $refs against
this agent's metadata and secrets. Different agents can host the same lambda
with different endpoints and credentials — the code never changes.
Reference a reusable configuration instead of inlining
The examples above define each composed tool inline. You can instead point
an entry at a reusable tool configuration
— one managed independently through the /v2/tool_configurations API and shared
across agents and lambdas — by giving it type: reference and the
configuration's key:
A LAMBDA THAT REFERENCES A SHARED CONFIGURATION
Code example with json syntax.1
- The map key (
fetch_ticket) is still the name your code calls —tool.fetch_ticket(...). It is independent of the referenced configuration's own name. tool_configuration_keyis the key of the reusable configuration to resolve.- Everything the shared configuration defines — its tool, its
argument_override, and any$refs inside it — still resolves against the hosting agent and session at execution time, exactly as an inline entry would. Update the shared configuration once and every lambda that references it picks up the change on its next run. - A reference to a key that does not exist is rejected when the lambda is
created (or when you call
POST /v2/tools/test), naming the unresolvable key.
Inline and reference entries can be mixed freely in the same
tool_configurations map.
What $ref resolves against
$ref values inside a composed tool's argument_override resolve from the
hosting agent and session at execution time:
| Reference | Resolves to |
|---|---|
agent.metadata.<key> | The hosting agent's metadata |
agent.secrets.<key> | The hosting agent's secrets, masked in session events |
session.metadata.<key> | The current session's metadata |
References to other tools' outputs (tools.*) are not allowed on a lambda's
own configurations — they would couple the lambda to the configuration names
of whatever agent hosts it.
Rules and limits
- At most 20 tool configurations per lambda.
sandbox_execandinvoke_skillcannot be composed into a lambda.- A composed tool may itself be a lambda with its own
tool_configurations; nesting has no fixed depth limit. A session wires at most 1024 lambda scopes; lambdas past that cap run without thetoolandartifactsbridges. - A lambda that appears again on its own composition path is wired with no
composed tools, so the repeated level's
tool.*calls fail instead of recursing. - One session run shares a budget of at most 50 concurrent and
10,000 total in-code tool invocations, counted across
sandbox_execand every composed lambda. A call over the concurrency bound fails immediately rather than queueing; once the total is spent, further calls in that run fail. Both limits reset on the next run. - Composed calls produce no session events; their results stay inside your lambda's code unless you return them.
The same tool module is available to Python the agent runs through the
built-in sandbox_exec tool — there it exposes the agent's other tools, so
the model can fetch data mid-script instead of guessing values.