> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs.ada.cx/docs/automation/tools/code-tools/inputs-outputs-and-environment/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.ada.cx/_mcp/server. # Inputs, outputs, and environment A code tool has four parts: the **inputs** it receives, the **environment variables** it reads for configuration and secrets, the **outputs** it returns, and the **code** that produces the result. The inputs, environment variables, and outputs are defined when the tool is created and referenced by name in the code. ## Inputs Each input to a code tool comes from one of two sources: * **Supplied by the AI Agent**: the Agent works the value out from the conversation. Agent-supplied inputs can be text, a number, true/false, or a list. * **Linked to a variable**: the value is read from a saved [variable](/docs/automation/variables). An object must be linked to a variable; the Agent cannot supply one directly. Define each input the tool needs, along with its source and type. The Agent only fills in the inputs marked as Agent-supplied. ## Environment variables Environment variables hold configuration and secrets, for example a base URL or an API key. Keeping these outside the code means the same tool can point at different systems or credentials without rewriting it. Read them in code with `os.getenv("NAME")` or `os.environ["NAME"]` after `import os`. Only the variables you declare here are exposed; nothing from Ada's own environment is reachable. Each environment variable takes its value from one of three sources: * **Value**: a fixed string you type, for example a base URL. * **Sensitive value**: a secret you type into this tool. It is stored encrypted. * **Sensitive Variable**: a sensitive [variable](/docs/automation/variables) your AI Agent captured, or a saved token. Use this source for credentials an API tool also uses, such as a client ID or a client secret. Choose **Sensitive Variable** for any secret that is already stored in Ada. Ada refuses a sensitive value that you select under another source when you save the tool. Prefer **Sensitive Variable** over **Sensitive value** for a credential you also use in an API tool. A typed secret is a copy. If you rotate the credential, you must edit each tool that holds a copy. A sensitive variable holds data the Agent captured in one conversation. A token is stored once and used by every conversation. Secrets are stored redacted. Once saved, a secret's value is not shown again in the dashboard. ## Outputs Each output is a field the tool returns. For every output, choose how it is used: * **Exposed to the AI Agent**: the Agent can use the value in its replies. * **Saved to a variable**: the value is written to a [variable](/docs/automation/variables) for later steps to read. An output can be exposed to the Agent, saved to a variable, or both. Declare one output per field so each value is addressed on its own. ### Let Playbooks branch when the tool fails For every other output, you choose which part of the returned value to select. A run that fails returns nothing, so those outputs hold no value. The run status is the exception. It reports how the run ended, so it always holds a value. **To let a Playbook recover from a failed run:** 1. Enable **Let playbooks branch when this tool fails** in the **Outputs** section. Ada adds the output and names it `ada_run_status`. The name is fixed, and the **Path** field does not apply. 2. Save the output to a variable. 3. Read that variable from an `IF/ELSE` step in your Playbook. See [Code tool error handling](/docs/automation/playbooks/step-reference#code-tool-error-handling). > **Note** > > This setting is rolling out. If the **Outputs** section does not show it yet, contact your Ada representative. The status is one of five values: | Value | Meaning | | :-------------- | :--------------------------------------------------------------------------------------------------------------------------------- | | `ok` | The code ran and returned a result. | | `timeout` | The code exceeded a time or memory limit. A retry can succeed. | | `error` | The code raised an error, or Ada could not run it. A retry usually does not help. It does help when the failure was on Ada's side. | | `invalid_input` | An input value was the wrong type. The code did not run. | | `disabled` | Ada turned the tool off after repeated timeouts. | ## The code The tool returns the value of its **last expression**: whatever the final line evaluates to becomes the result. You don't wrap it in a function or write a `return`. Your inputs are available as variables by name, and you call the [available functions](#available-functions) below directly. ### Supported Python The code runs on a sandboxed Python interpreter — a restricted subset, not full Python. Here is exactly what's available. #### You can use * Variables, arithmetic, string operations, f-strings (including format specs like `f"{price:.2f}"`), and `.format()`. * Conditionals (`if`), loops (`for` / `while`), comprehensions, `try` / `except`, your own `def` and `lambda` functions, and `class` definitions. * Import these standard-library modules as usual: **`json`, `re`, `datetime`, `math`, `os`, `pathlib`, `sys`, and `typing`**. (`pathlib` can only build paths; reading a file is blocked.) * The current date and time: after `from datetime import datetime, date`, **`datetime.now()`** and **`date.today()`** return the time in your AI Agent's configured timezone. #### You can't use * **Any other import.** No `requests`, `random`, `collections`, `itertools`, and so on. A disallowed `import` is caught when you save, even inside `try` / `except`, and a saved tool that still contains one fails when it runs. Common gaps (hashing, Base64, URL-encoding, UUIDs) are covered by the [built-in functions](#available-functions) instead. * **Generators, `del`, and `match`.** No generators (`yield`), no `del` statement, and no `match` statement. These are caught when you save. * **`eval`, `exec`, `open` and file access, `input`,** and dynamic tricks like `globals()`. * **`%` string formatting.** Use f-strings or `.format()` instead. This fails when the tool runs, not when you save it. ### The result The result is the tool's last expression, and it must be a **JSON value**: text, a number, true/false, a list, or an object with text keys, not a file or an image. A few common Python types aren't JSON and must be converted before you return them: a `datetime` (call `.isoformat()`), a `set` or `tuple` (wrap in `list(...)`), or `bytes` (decode it first). Returning nothing (`None`) counts as no result. To make a network request, read an environment variable, or read and write variables, use the built-in functions below rather than a library. For the full sandbox limits, see [Limits and best practices](/docs/automation/tools/code-tools/limits-and-best-practices). ## Available functions Ada provides these functions directly in your code, no import needed. ### `fetch` — make an HTTP request ```python response = fetch("GET", "https://api.example.com/orders/123") # -> {"status_code": 200, "headers": {...}, "body": "..."} ``` `fetch(method, url, headers=None, body=None, timeout=None)` returns an object with `status_code`, `headers`, and `body`. The body is a string, so parse it with `json.loads` if it's JSON. Requests reach only the domains on your allowed-domains list (plus your own Ada instance); a blocked or failed request raises an error you can catch. Redirects aren't followed, so a request to a redirecting URL returns the 3xx response, not the final page. Per run: up to 10 requests, a 1 MB response each, and about 60 seconds of network time. ### `get_variable` and `set_variable` — read and write variables ```python tier = get_variable("customer_tier") set_variable("last_lookup_status", "ok") ``` `get_variable(name)` returns a saved [variable's](/docs/automation/variables) current value, and raises if it doesn't exist. `set_variable(name, value)` saves a value, applied only if the run succeeds. Sensitive-scoped variables and tokens can't be read or written from code. Declare them as environment variables instead. These two functions need a live conversation. A test run from the code editor has none, so a snippet that calls them fails there with a name error. Test that snippet in a conversation instead. ### Utilities — hashing, encoding, and IDs Common helpers that would otherwise need a library, available directly (no import): * `uuid4()` — a random UUID string. * `sha256(text)` and `hmac_sha256(key, text)` — hex digests. * `b64encode(text)` and `b64decode(text)` — Base64 encode and decode. * `quote(text)` and `urlencode(mapping)` — URL-encode a single value, or a set of query parameters. ### `log` — write to the run log ```python log("looked up order", order_id) ``` `log(...)` (and `print(...)`) adds a line to the tool's run log for debugging. Logs show in the conversation's run detail, have secrets redacted, and are never shown to the AI Agent. ## Voice capture For voice-enabled AI Agents, code-tool inputs support the same voice capture settings as [API tools](/docs/automation/tools/api-tools) (speech, keypad, or SMS), along with confirmation before the value is used. This lets an Agent collect an input reliably over a voice call. SMS capture is available only while [Agent SMS](/docs/channels/voice/voice-onboarding#control-whether-your-ai-agent-can-send-sms) is enabled. --- Have any questions? Contact your Ada team, or email us at [](mailto:help@ada.cx?subject=Help%20Docs%20inquiry).