a9script

Docs / Scripts

Writing scripts

A script is a business rule written out in order. The functions do the work against other systems; the script decides what happens and in which order. This page shows how a rule becomes a script, and what a script needs beyond that.

From a rule to a script

Write the rule as one sentence first:

When an order arrives, make sure the customer is in the CRM, then send the receipt.

Then each part of the sentence is one line or one block:

Part of the ruleIn the script
an order arrivesinput.event.body — what started the run
the customer is in the CRMFindContact, and CreateContact if nothing was found
send the receiptOrderReceipt
what happenedlog while it runs, return at the end
A configured function in the script editor: its signature and what it answers, before anything is activated.A configured function in the script editor: its signature and what it answers, before anything is activated.
A configured function in the script editor: its signature and what it answers, before anything is activated.
const order = input.event.body;

let contactId;
const found = FindContact({ email: order.customer });
if (found === null) {
  contactId = CreateContact({ email: order.customer, name: order.customer });
} else {
  contactId = found.id;
}

const total = order.items.reduce(function (sum, item) {
  return sum + item.price;
}, 0);
OrderReceipt({ to: order.customer, order: order.id });

log("order confirmed", { order: order.id, contact: contactId, total: total });

return { order: order.id, contact: contactId, total: total };

The script has no URL, no credential, no status check and no retry in it. Those are the functions’ settings (Connections and functions). A call answers on the next line; if it could not be answered the way its function accepts, the run stops there and says why.

Reading what started the run

input.event is what arrived: the body of a webhook or a call, its headers, a mail, a schedule firing. input.settings is what the script’s settings script returned. Reading what triggered you shows both, door by door.

Deciding

Deciding is ordinary JS: if, for, Map, Set, filter, map.

const seen = new Set();
const skus = [];

for (const item of input.event.body.items) {
  if (!seen.has(item.sku)) {
    seen.add(item.sku);
    skus.push(item.sku);
  }
}

return skus;

Call a function inside a plain loop when the work is per item, never inside a map callback; when the work is thousands of records, a bulk call does it where the records are. The one shape that does not scale explains why.

Values that differ between sandbox and production

A page size, a team id, a mailbox: values like these do not belong in the code. They go in a settings script, which the script links, and arrive as input.settings. Each environment has its own settings script, so the same script runs unchanged in sandbox and production.

const pageSize = input.settings.pageSize;

log("reading pages of", { pageSize: pageSize });

return pageSize;

Code several scripts share

Shared functions go in a library script. A script that links a library has its functions in scope; there is no import.

Saying what happened

  • log("message", { … }) writes a line to the run’s log; logError marks it as an error. The run panel shows every line.
  • return ends the run with its result. A caller waiting for an answer gets it, unless the script already answered with respond.

When something is wrong

throw ends the run as failed, with your message. Use it for a business rule that is broken — an order with no items, a customer who must exist:

const order = input.event.body;

if (order.items.length === 0) {
  throw new Error("order " + order.id + " has no items");
}

return order.items.length;

A failed run can be watched for; see Reacting to failures. try/catch catches what you expect; what it never catches is a budget (Budgets).

Waiting

checkpoint() saves the run, gives the machine back, and continues on the next line — checkpoint(600000) ten minutes later. Time spent waiting costs no budget. A run that wants to start again next week is a schedule, not a wait (Triggers and concurrency).

What the language leaves out

Scripts are ordinary JS with three things missing:

  • No classes. Objects, arrays and functions do the same job.
  • No async, await or promises. A call answers on the next line; the platform does the waiting.
  • No import. Shared code is a library script.

The complete list, and why each item is left out, is on The language.

Testing a script

A traced test run: every call that left the platform, with what it sent and what came back.A traced test run: every call that left the platform, with what it sent and what came back.
A traced test run: every call that left the platform, with what it sent and what came back.

Every script has a Test button with a sample input. A test run shows the result, every log line, and — with Trace calls on — every call that left the platform, with what it sent and what came back.

Next

  • Budgets — the four limits every run has, and what to change when a script reaches one.
  • Triggers and concurrency — what starts a script, and how many runs of it may run at once.
  • The model behind all of this, with every claim executed: The script model.
Rendered from docs/guide/writing-scripts.md in the product's own repository, at build time. Found a problem on this page? Write to the address in the footer.