LogicScript Prompt Language (LSPL)
For IT, administrators, and consultants who want to build powerful, repeatable AI tools — AI Tools — that their clients and end users run with one click in Netridium.
This guide is the reference for the LogicScript Prompt Language (LSPL). It assumes no programming background: if you can describe a process in clear, structured steps, you can write an LSPL tool.
In one line: you write the procedure; the AI runs it — step by step, using your connected systems — and returns a finished result. The script is the prompt. The AI is the runtime.
1. What LSPL is
LSPL is a structured language for describing what a tool should do, in plain, readable steps. You write the procedure — the checks, the decisions, the order of operations, the shape of the output — and the AI carries it out: reasoning where you ask it to, branching on its conclusions, reading from your connected systems, and assembling the result.
It sits between Netridium's other two ways to build a tool:
- A Prompt is the fastest path — you describe the outcome in a sentence or two
and let the AI work out the whole approach.
- A Script (Python) is fully deterministic — you write exact code.
- LSPL is the middle ground: you make the process explicit and repeatable,
while the AI still supplies the judgement at each step. Two people read the same script the same way, and it runs the same across many inputs.
See LOGICSCRIPT_TYPES.md for the full three-type comparison.
2. The mental model
When a user runs your LSPL tool, Netridium:
- Collects any pre-run inputs you defined (a short form) and passes them to the
script as its arguments.
- Hands the AI your AI Tool and has it carry out each step.
- Lets the AI read from your connectors as the script directs (read-only).
- Captures what the script
RETURNs (and anyEMIT/LOGlines) and renders it asthe deliverable — an Excel workbook, PDF, HTML page, chart, text, or JSON.
A few consequences worth internalizing:
- The AI fills in the reasoning; you fix the process. The branches, validations,
output shape, and order of steps are yours. The wording and judgement inside each
AI.callwill vary run to run — that's the point. Pin down anything that must be exact. - Runs are read-only. An LSPL tool can read and analyze your systems, but
it
cannot change data or send real messages. To write data or send email, use a Python Script (the only write-capable type). See §9.
- A run produces one result. Like every AI Tool, it's fire-and-finish: the
user clicks Run and gets a result back. There is no mid-run pause for more input — collect everything you need up front with Inputs (§7).
- At least one connector is required. An LSPL tool is linked to one or more
connectors (the systems it may read).
3. Anatomy of an AI Tool
An AI Tool is usually a single FUNC run(...) with an optional INPUTS block, a
VALIDATE block, a DO block of steps, and a RETURN:
INPUTS
period: select [last_month, this_month, last_quarter]
customer: text optional
FUNC run(period, customer)
--- Produce an accounts-receivable aging summary for the chosen period. ---
VALIDATE
period not empty
DO
-- 1. Pull the data
invoices = mcp("xero").call("list_invoices", { status: "AUTHORISED" })
-- 2. Reason over it
aging = AI.call(
"Bucket these invoices into Current, 1–30, 31–60, 61–90, and 90+ days
overdue. Total each bucket and compute the percentage of the balance."
)
LOG "Bucketed {invoices.length} invoices for {period}"
RETURN aging
That's the whole shape: declare inputs → validate → do the steps → return a result. Everything else in this guide is the vocabulary that goes inside.
4. Language reference
These are the constructs the language understands. Indentation is 2 spaces per level — there
are no braces or end keywords.
Comments
-- inline comment to the end of the line
--- A block comment / docstring.
Use it under FUNC to state the goal in plain English. ---
A ----delimited block right under FUNC is the best place to state the tool's
purpose; the AI reads it as the mission.
Variables & assignment
Assign the result of a step to a name and reuse it later:
invoices = mcp("xero").call("list_invoices", {})
overdue = AI.call("From the invoices above, keep only those past their due date.")
count = overdue.length
Variables are inferred — you don't declare types. Dotted access (ticket.subject,
invoice.amount) reads a field.
String interpolation
Insert a value into a string with single braces { }:
LOG "Found {count} overdue invoices for {customer}"
INPUTS — pre-run parameters
Declare the values a user supplies before the run. (In Netridium, the actual form is built in the AI Tool editor's Inputs section — see §7 — and mirroring it here keeps the script readable and tells the AI what arguments to expect.)
INPUTS
accounts: select FROM connector("xero").accounts -- dynamic options
recipient: user -- pick a workspace user
template: select [welcome, reminder, overdue] -- static options
asOf: date
note: text optional
Field kinds: text, number, bool, date,
select [a, b, c] (static), select FROM connector(...) (dynamic options from a
connected system), and user (a workspace user). Add optional to make a field
non-required.
VALIDATE — preconditions
Checked before any work happens; the first failure stops the run and is reported instead of continuing. One condition per line; plain English is fine:
VALIDATE
period not empty
asOf is a valid date
accounts has at least one item MESSAGE "Pick at least one account"
Add MESSAGE "…" to give the user a clear error.
DO — the sequence of steps
The body. Steps run top to bottom: assignments, connector reads, AI.calls,
conditionals, loops, LOG, and EMIT.
AI.call("…") — reason or decide here
The heart of an LSPL tool. Wherever judgement is needed, hand the AI a focused instruction; it reasons over the data in scope and returns the result:
classification = AI.call(
"Read the ticket and return: priority (low/medium/high/urgent),
category (billing/shipping/account/technical/general), and whether you can
resolve it yourself (yes/no). Respond as: priority / category / yes-or-no"
)
Be specific. The tighter the instruction (what to produce, what format, what edge cases),
the more consistent the output. Vague AI.calls are where runs drift.
IF / ELSE IF / ELSE — branching
Branch on the AI's own conclusions. Conditions can be structured or plain English:
IF classification.canResolve IS yes AND ticket.attempts < 2
-- attempt resolution
ELSE IF classification.priority IS urgent
-- fast-track
ELSE
-- escalate
FOR EACH … IN … — iteration
Repeat steps over a collection:
FOR EACH invoice IN overdue
reminder = AI.call("Draft a one-line overdue note for invoice {invoice.number}.")
EMIT Reminder
invoice: {invoice.number}
text: {reminder}
Reading from connectors — mcp(...) and
QUERY
Direct the AI to pull data from a connected system. Two readable forms:
-- Call a connector's tool directly:
invoices = mcp("xero").call("list_invoices", { status: "AUTHORISED" })
-- Or describe the retrieval (the AI maps it to the right tool):
QUERY overdueInvoices
FROM Xero.Invoices
WHERE status = "AUTHORISED" AND dueDate < today
ORDER BY dueDate ASC
LIMIT 500
Use the connector name exactly as it appears on the Connectors page. All connector access in an LSPL tool is read-only (see §9). For the tools each connector exposes, see CONNECTOR_SETUP_FOR_ADMINS.md.
LOG — narrate the run
A note about what just happened. Useful for traceability; the AI folds these into the result/run log where helpful:
LOG "Reconciled {matched} of {total} transactions; {unmatched} unmatched"
EMIT EventName — structured output
Emit a labelled, structured record. Emitted events are captured as part of the result — ideal for line items, flags, or a status payload:
EMIT ReconciliationResult
status: {status}
matched: {matched}
unmatched:{unmatched}
comments: {comments}
(The compact form EMIT EventName WITH { matched, unmatched } also works.)
RETURN — the final result
Ends the procedure and designates the value the tool produces. What you RETURN (together with
any EMITs) becomes the deliverable, rendered into one of the output formats in
§8:
RETURN aging
5. Actions: SEND, ALERT,
NOTIFY (read-only)
The language includes verbs for acting — SEND a message, ALERT a team,
NOTIFY a user. In Netridium these are read-only: the AI drafts
and clearly labels them in the result, but nothing is actually sent and no data is
changed.
SEND TO {ticket.customerEmail}
Subject: Re: {ticket.subject}
Body: {response}
ALERT support-team
Ticket {ticket.id} needs human attention — {classification.priority} priority.
In the result you'll see these as a drafted email and a flagged alert — perfect for review-and-send workflows where a human approves the output. To actually send email or write data, build a Python Script (the write-capable AI Tool type), which can call a connector's write tools under explicit, audited permission. See §9.
6. Conditions & expressions
Conditions (in VALIDATE, IF, WHERE) can be written structurally or in
plain English — whichever is clearer:
- Comparisons:
amount > 1000,dueDate < today,attempts < 2 - Equality / membership:
status IS resolved,category IN [billing, shipping] - Text:
response does not contain "CANNOT RESOLVE",email matches email pattern - Boolean logic:
AND,OR,NOT - Presence:
subject not empty,accounts has at least one item - Plain English:
the invoice looks like a duplicate— valid anywhere a structuredexpression is awkward; the AI evaluates it.
Lean on plain English for judgement and structured form for facts — it keeps the intent obvious to both the AI and the next person who reads your script.
7. Inputs — the run form
Pre-run inputs turn a one-off script into a reusable, self-service tool. In Netridium you define them in the
AI Tool editor's Inputs section; Netridium renders a short form, validates the values,
and passes them to your script as run(...)'s arguments. Reference them by name and interpolate
with { }.
| Field kind | Renders as | Example |
|---|---|---|
text |
A text box | note: text optional |
number |
A numeric input | threshold: number |
bool |
A checkbox | includeDrafts: bool |
date |
A date picker | asOf: date |
select [a, b, c] |
A dropdown of fixed options | period: select [last_month, this_month] |
select FROM connector(...) |
A dropdown filled from a connected system | account: select FROM connector("xero").accounts |
user |
A picker of workspace users | owner: user |
Mirror these in an INPUTS block at the top of your script so the procedure reads clearly. Full
detail: PROMPT_INPUTS.md.
8. Output formats
Whatever your script RETURNs is rendered into the format that best fits the task. Aim your
result at one of these — say so in your closing AI.call/RETURN if it matters:
| Format | Use it for |
|---|---|
| Excel | Tabular data — the common case (rows × columns, one or more sheets). |
| HTML | A rich, formatted document or styled layout. |
| A print-ready report, letter, or written summary. | |
| Image | A chart or diagram (SVG, or PNG/JPEG). |
| Text | A status, a short summary, or plain prose (e.g. a reconciliation status + comments). |
| JSON | Raw structured data, or deeply nested results a table would flatten. |
A reconciliation tool might RETURN a text status with comments; an aging report
might RETURN an Excel workbook. The result also downloads with one click and is
saved to History with its inputs and cost.
9. Connectors, read-only & when to use a Script
- Read-only by design. LSPL tools can only read and list data — they can never
create, update, or delete records, and
SEND/ALERT/NOTIFYare drafts only (§5). This keeps an AI-run procedure safe to hand to clients. - Each workspace connects its own accounts, with credentials encrypted and never
shared across workspaces.
- Need to write data or actually send a message? Use a Python Script — the
only type that can write, and only through a connector explicitly granted write access. A natural pattern is: an LSPL tool drafts and reviews; a Script commits.
10. Two worked examples
A — Accounts-receivable aging (report → Excel)
INPUTS
asOf: date
FUNC run(asOf)
--- Build an AR aging summary as of the given date. ---
VALIDATE
asOf is a valid date
DO
invoices = mcp("xero").call("list_invoices", { status: "AUTHORISED" })
summary = AI.call(
"Using the invoices above and the date {asOf}, bucket each open balance into
Current, 1–30, 31–60, 61–90, and 90+ days overdue. Produce one row per
customer with a column per bucket and a total column, plus a final totals row."
)
LOG "Aged {invoices.length} invoices as of {asOf}"
RETURN summary -- rendered as an Excel workbook
B — Support-ticket triage (drafts a reply for review)
This tool runs read-only: the SEND and ALERT appear in the result as a
drafted reply and a flag for a human to act on.
FUNC run(ticket)
--- Resolve a support ticket if possible; otherwise escalate. ---
VALIDATE
ticket.subject not empty
ticket.body not empty
DO
classification = AI.call(
"Classify this ticket: priority (low/medium/high/urgent), category, and
whether you can resolve it yourself (yes/no)."
)
LOG "Classification: {classification}"
IF classification.canResolve IS yes
response = AI.call(
"Draft a specific, actionable reply that resolves the ticket. If you can't,
say CANNOT RESOLVE and explain why."
)
IF response does not contain "CANNOT RESOLVE"
SEND TO {ticket.customerEmail}
Subject: Re: {ticket.subject}
Body: {response}
EMIT TicketResolved
ticket_id: {ticket.id}
RETURN ticket
ALERT support-team
Ticket {ticket.id} needs human attention — {classification.priority}.
EMIT TicketEscalated
ticket_id: {ticket.id}
reason: could not resolve with confidence
RETURN ticket -- result shows the drafted reply and/or the escalation flag
11. Best practices for builders
- State the goal up front in a
---block underFUNC. The AI treats it as themission.
- Collect inputs, don't assume them. Anything the user should choose belongs in
INPUTS, validated inVALIDATE. - Make
AI.calls specific. Spell out the output shape, the buckets, the edgecases. Specific instructions → consistent runs.
- Fix the structure; let the AI fill the words. Use
IF/FOR EACH/VALIDATEfor anything that must be exact; use
AI.callfor judgement. - Pick the output deliberately (§8) and say so in the final step.
- Keep it read-only. If a tool needs to write or send for real, that's a
Python Script — design the LSPL tool to draft and review instead.
- Author for reuse. A good LSPL tool reads the same to two people and runs the
same across many inputs — that's its advantage over a free-form Prompt.
12. Configured in Netridium, not in your script
A few things are handled by the platform rather than written into an AI Tool:
- Scheduling — run a tool automatically daily, weekly, or monthly (set on the
tool's Schedule page), instead of writing a schedule into the script.
- Who can see and run a tool — controlled by Roles, not in the script.
- Connections, AI keys, and notification preferences — workspace Settings.
Write the procedure in LSPL; configure when it runs and who can run it in Netridium.
13. Quick reference
| Construct | Purpose |
|---|---|
FUNC run(args) |
The procedure and its arguments. |
--- … --- / -- … |
Block / inline comments. |
INPUTS |
Declare pre-run form fields. |
VALIDATE |
Preconditions; first failure stops the run (MESSAGE "…"). |
DO |
The ordered list of steps. |
name = … |
Assign a value to reuse. |
{var} |
Interpolate a value into a string. |
AI.call("…") |
Have the AI reason/decide here. |
IF / ELSE IF / ELSE |
Branch on conclusions. |
FOR EACH x IN list |
Loop over a collection. |
mcp("name").call("tool", {…}) / QUERY |
Read from a connector (read-only). |
LOG "…" |
Narrate the run. |
EMIT Event |
Output a structured record. |
RETURN value |
Produce the final result (→ Excel/PDF/HTML/text/image/JSON). |
SEND / ALERT / NOTIFY |
Draft & label a message/flag (read-only — no real send). |
Related guides
- LOGICSCRIPT_TYPES.md — Prompt vs LSPL vs Script.
- PROMPT_INPUTS.md — building self-service input forms.
- CONNECTOR_SETUP_FOR_ADMINS.md — connecting systems.
- CLIENT_ADMIN.md — building and managing tools end to end.