Skip to main content
Updated Oct 18, 2026

Libraries

Libraries let your company write a piece of logic once and reuse it everywhere: in workflows, code steps, agents and forms. Each library is published as numbered versions that never change, and every place that uses a library picks one exact version. Editing or publishing a library never changes something that already uses it; moving to a newer version is always a deliberate step.

The Libraries list with CEL, JavaScript and Python libraries

All your company's libraries in one list, with their latest version and state.

Choosing CEL, JavaScript or Python​

CELJavaScriptPython
What you writeTyped functions with a CEL expression bodyA JavaScript module with named exportsA Python module
Where you use itAny CEL expression: conditions, templates, forms, agent toolsExecute Code steps in JavaScriptExecute Code steps in Python
Runs inThe CEL evaluator, no I/OA V8 isolate; fast start, up to 30 s per runA container with data packages; up to 120 s per run
Good forSmall rules and formatting you need inside expressionsGeneral code, data shaping, HTTP callsSpreadsheets, PDFs, images and other data work

CEL libraries have their own editor: see CEL libraries. The rest of this page covers JavaScript and Python libraries.

Find and create a library​

Open Automations → Libraries. The list shows every library with its module name, latest version, last update and state: Published, Draft, Unpublished changes or Archived. Filter by language, search by name, and turn on Show archived to include archived libraries.

To create one, choose New library and pick CEL, JavaScript or Python. For JavaScript and Python:

  • Language is fixed once the library is created.
  • Name is what people see in the app.
  • Module name is how code refers to the library, for example text_helpers. It is filled in from the name as you type, and you can change it before creating the library. It uses lowercase letters, digits and underscores, starts with a letter, and has at most 48 characters; language keywords and runtime names are reserved. It can't be changed later, and an archived library keeps its name.
  • Description is optional.

The preview under the module name shows how a code step will call the library.

The editor​

A code library opens with a status line at the top. It says where the library stands, such as "No tests yet", "1 test fails" or "Ready to publish v2", and holds three actions:

  • Check syntax checks the source and its imports without running it.
  • Run tests runs the library's tests.
  • Publish vN creates the next version. It names the version it will create, and when it is unavailable, hovering it says why.

Saving the library never runs code. Check syntax and Run tests use the code sandbox and count toward compute usage.

The editor has six tabs:

TabWhat it's for
SourceThe module source. Problems found by Check syntax are underlined on their line. Wrap wraps long lines; Wide view gives the editor the whole page.
TestsNamed tests, each with its own JSON Input, and the result of the last run.
ImportsOther libraries this module uses, each pinned to an exact version.
VersionsEvery published version, with its date, test result and changed exports. Compare a version with the previous one or export its source.
Used byWorkflows and other libraries that use this library, grouped by version, with how many versions behind each one is.
SettingsName, description, download the draft, and archive the library.

The code library editor with its status line, actions and tabs

Writing JavaScript libraries​

A JavaScript library is an ES module. Export what other code should use:

/** Trims a name and collapses repeated spaces. */
export function normalizeName(name) {
return String(name ?? "").trim().replace(/\s+/g, " ");
}

export const DEFAULT_COUNTRY = "BR";
  • Functions, classes and constants can be exported. Exported async functions work, and a code step can await them.
  • To use another library, add it under Imports, then import it: import * as text_helpers from "autotalk_libraries/text_helpers"; or import {normalizeName} from "autotalk_libraries/text_helpers";.
  • Module-level await and dynamic import() are not supported.
  • A library has the same sandbox APIs as a code step, including fetch, and each run gets fresh module instances.
  • A /** ... */ comment right above an export becomes the description shown in the code step and in autocomplete.

Writing Python libraries​

A Python library is a module:

from decimal import Decimal, ROUND_HALF_UP

TAX_RATE = Decimal("0.18")

def money(value):
"""Rounds a value to cents."""
return Decimal(str(value)).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
  • The library's exports are its top-level functions, classes and assignments. Names that start with _ and names the module only imports are left out, so Decimal above is not an export. To choose the exports yourself, define __all__.
  • To use another library, add it under Imports, then from autotalk_libraries import text_helpers.
  • For HTTP, import fetch from the runtime: from autotalk_runtime import fetch, FetchError. The standard library and the Python runner's bundled packages are available.
  • Pass caller data to your functions as arguments; a library can't read the step's input directly.
  • A function's docstring becomes the description shown in the code step and in autocomplete.

Tests​

A test is a short script that calls the library and fails when something is wrong: it throws an error in JavaScript, or fails an assert in Python. Each test can have a JSON Input, available as input in JavaScript and INPUT in Python.

if (autotalk_libraries.text_helpers.normalizeName(input.name) !== "Ada Lovelace") { // input: {"name": " Ada Lovelace "}
throw new Error("Unexpected name");
}
from autotalk_libraries import order_math

assert str(order_math.money(INPUT["value"])) == "10.50" # INPUT: {"value": 10.499}

Add test starts a test that calls your first exported function with its parameters taken from the input. After a run, each test shows Passed with its duration, or Failed with the line that failed and its logs. Editing a test after a run marks its result as out of date.

Tests run in the normal sandbox, one after another, in fresh sandboxes. They can reach the network and write files, and each run counts toward compute usage. Up to 20 tests run per library, within 120 seconds in total.

Publishing and versions​

Save the library, then choose Publish vN. The confirmation shows what the new version changes since the last one: exports added and removed, how many lines changed (with the full difference one click away), how many tests will run, and who uses the current version. Publishing checks the saved source and runs all tests again; a failing test stops the publication. A library with no tests can be published, and its version is marked untested.

Versions are numbered v1, v2, v3 in publishing order and never change. Publishing a new version does not move anything to it: everything that uses the library keeps its version until someone updates it.

Using a library in a code step​

In an Execute Code step, the Libraries box above the code lists the libraries the step uses:

  • Add a library pins a published library of the same language at its latest version.
  • The version select shows each version, with (latest) next to the newest. When a newer version exists, Update to vN opens a review of the exports and source that change before you apply it.
  • Open a library's row to see its exports with their parameters and description. The insert button next to each one adds a call at the cursor, on its own line, with the first parameter selected. In Python it also adds the import at the top if it is missing.

A code step with a pinned library and its exports

Call the library from the step:

return autotalk_libraries.text_helpers.normalizeName(input.name);
import json
from autotalk_libraries import order_math

result = order_math.money(INPUT["value"])
with open("/work/out/output.json", "w") as output:
json.dump(str(result), output)
  • A JavaScript step can reach only the libraries pinned on that step; a library's own imports are not exposed to the step.
  • A Python step returns its result by writing /work/out/output.json.
  • Changing the step's Language removes its pinned libraries. The confirmation lists them, and the code stays as written, so update it for the new language.

Used by and archiving​

Used by shows every workflow and library that pins this library, grouped by version, so you can see who is still on an older version before you change anything.

Archiving a library (Settings → Archive library) stops new versions and new pins. Everything that already pins it keeps working. Archiving can't be undone: the draft can no longer be edited, the module name stays taken, and published versions are kept. Don't put secrets in library source.

Limits​

LimitValue
Direct imports per step or library24
Libraries in one dependency graph32
Import depth16
Source per module or test64 KiB
Whole draft (source and tests)512 KiB
Code bundle for one run2 MiB (separate from input-file limits)
Tests per library20
Test run time, in total120 seconds

Permissions and API scopes​

  • Anyone who can read a library can pin it and use it.
  • Creating, editing, checking, testing, publishing and archiving a library are limited to the company owner. Other members see libraries read-only.
  • With an API key, creating and editing libraries, checking, testing, publishing and archiving need automation:admin. Reading libraries and their versions needs data:read.

MCP and API​

An MCP client can do everything the editor does:

  • get_code_library_reference lists the published libraries you can read, or describes exact versions: exports with their parameters and description, how to call them, and whether a newer version exists. It needs data:read.
  • Create and edit libraries with create_document and update_document on code_libraries. Send the draftRevision you read with the library, so you never overwrite someone else's edit.
  • Use execute_function for validateDraft, testDraft and publish (with expectedDraftRevision), and for listReleases, getRelease, listDependents and archive.
  • Pin exact versions as codeLibraries: [{libraryId, releaseId}] on a code step, or on the run_code tool for a one-off run.

The libraries guide in the MCP server (get_guide) explains when an assistant should reuse or create a library.

Common errors​

CodeWhat it means
code_library_draft_conflictThe library changed since you opened it. Reload and try again.
code_library_owner_requiredOnly the company owner can check, test, publish or archive.
code_library_archivedThe library is archived, so it can't be edited, published or newly pinned.
code_library_invalid_slugThe module name breaks the naming rules or is reserved.
code_library_language_mismatchA pinned library's language differs from the step or library that uses it.
code_library_dependency_cycleLibraries import each other in a loop.
code_library_syntax_errorThe source doesn't parse; the editor underlines the line.
code_library_suite_timeoutThe tests took longer than 120 seconds in total.
code_library_not_foundThe library doesn't exist or you can't read it.
code_library_operation_reusedThe same operationId or Idempotency-Key already ran these checks. Retry with a new key.