Blog article

Server-side scripting — when automations aren't enough

Published on November 1, 2023

Most business logic fits the visual automation builder, and when it fits, that's where it should stay. Visual automations are easier to read at a glance, safer to edit by someone who didn't originally write them, harder to accidentally break, and transparent to non-technical stakeholders who want to understand what their system does. The visual builder handles the overwhelming majority of what automation needs to express — triggers, conditions, record manipulations, notifications, integrations with known services — and it handles them in a form that survives changes of hands gracefully.

Some logic, though, genuinely needs a language. A pricing calculation that depends on seven interlocking variables with irregular precedence. A data transformation that pivots one shape into another through a non-obvious set of steps. An integration with an external API that has quirks the generic webhook action can't comfortably accommodate. A batch process that walks thousands of records applying a computation that would be a visual automation's version of hell. For these cases — and only for these cases — the right tool is a script, and the right place to write it is inside the platform rather than inside some separate system that the platform has to awkwardly integrate with.

Script objects are first-class citizens of the platform. A script has a name, a version history, a role-based permission model, an audit trail, and a place in the event log. It lives in the browser editor alongside queries and views and automations; it's edited in the same interface implementers already know; it deploys without leaving the platform. That integration matters because scripts that exist outside the platform — on some external server somewhere, touched only by whoever set them up and forgotten when that person moves on — are the original source of most implementation tech debt.

Typed access to the data model is what makes scripts genuinely productive. Rather than requiring the script to reach for the REST API and serialize objects manually, the script has direct access to types, properties, and records with schema awareness. Reading a record returns a typed object with properties the editor can autocomplete. Writing to a property validates against the schema. Relationships are navigable without hand-crafted joins. That ergonomics is what keeps script development from being a miserable experience of string-wrangling; working with the data in a script feels similar to working with it in any well-designed programming environment.

HTTP client is built in, with the retries, timeouts, and error-handling that any real integration needs. Scripts can call external APIs without reaching for a third-party library, without configuring a separate network-access layer, and without worrying that a transient failure will crash the entire script. The HTTP layer handles the boring reliability machinery so the script can focus on its actual business logic.

Secrets access integrates the scripting environment with the platform's secrets management. A script that needs an API key references it by name; the secrets feature resolves the reference at runtime. The credential value never appears in the script source, never ends up in version history, never shows up in code reviews. For any script that touches external services — which is most of them — this integration is what keeps credentials from being the weak link in the extensibility story.

Email from scripts works through the tenant's configured email engine, using the same machinery and templates the rest of the platform uses. A script that needs to send a custom notification doesn't have to re-implement email delivery; it calls the platform's email function with the recipient, subject, body, and any template variables, and the email engine takes it from there. The message goes out under the tenant's from-address, with the tenant's branding, tracked in the email logs the same way any other outbound email is.

Scripts invoked from automations are the most common integration pattern. An automation that needs one unusual step — a calculation too complex for the visual builder, an integration with an idiosyncratic external service — drops a script step into its flow. The automation passes inputs to the script; the script does its work; the automation continues with the script's outputs. That mixing of visual and scripted logic keeps each part of a workflow in the form best suited to its purpose, rather than forcing either visual-only or code-only.

Scripts as UI actions give users a button that runs a script. A "generate this report" action on a record runs a script that assembles the report and returns a PDF. A "reconcile this account" action on a financial record runs a script that does the multi-step reconciliation logic. The script is exposed as an action; the user sees a button; the button's implementation happens to be a script rather than a visual automation. For the cases where user-initiated complex logic belongs in a single clean trigger rather than a chain of automation steps, this is the pattern.

Scheduled scripts run on a cron-like schedule, handling the batch-processing cases that don't fit the event-triggered shape of automations. A nightly roll-up computation; a weekly cleanup pass over stale records; a monthly report generation that touches every customer in the tenant. The scheduler article covers the scheduling machinery; scripts hook into it as one of the things that can run on a schedule, with the same logs and notifications as any other scheduled work.

Error handling and logging are first-class concerns. A script that throws an unhandled error lands in the event log with a structured record — what script, what inputs, what error, at what line. Errors that happen inside scheduled executions generate notifications rather than silently failing. Scripts that need to log their own progress or signal partial failures have a logging API that plugs into the same observability surface. For the operational reality of scripts that run unattended for months and sometimes encounter conditions their author didn't anticipate, this observability is what keeps them maintainable.

Sandboxing and limits prevent misbehaving scripts from affecting the rest of the platform. Each script runs with CPU, memory, and wall-clock time caps; a script that exceeds any of them is terminated with a clean error rather than dragging down the tenant. That containment is what makes scripting a safe extensibility mechanism rather than a loaded gun: an implementer writing an infinite loop inherits the inconvenience of their mistake, but the rest of the tenant keeps running.

When to reach for a script, and when not to is the judgment call that matters most. Start with the visual automation builder; stay there as long as the logic fits comfortably; drop to a script only when the visual form is genuinely worse. Most implementations will have many visual automations and a handful of scripts, and that ratio is the right one. For the surrounding topics, the automations article covers the visual builder that scripts integrate with, the code editor article covers the in-browser editing experience, the secrets management article covers the credential handling, and the event log article covers the error reporting. Server-side scripting is the escape hatch the platform deliberately provides — powerful enough to handle what needs handling, governed enough to stay safe, and never the first tool reached for. That balance is what makes extensibility sustainable.