APSL Specification
The Action Plan Scripting Language (APSL) is a markdown-based language for writing alarm response protocols. A script is authored in markdown, validated at compile time, and interpreted at runtime by the APSL Executor, which drives the operator UI over a WebSocket.
Design goals: readable markdown structure, an explicit safety model around dispatch, compile-time validation, and real-time backend-driven execution.
Safety rules
These are the non-negotiable guarantees the compiler and runtime enforce:
Rule | Enforcement |
|---|---|
| Compiler error |
| Compiler error |
All dispatch requires operator confirmation | Structural (the two rules above) |
| Compiler warning if actions follow |
Script should contain at least one | Compiler warning |
All phase references must exist | Compiler error |
Only | Runtime behavior |
Document structure
An APSL script is YAML frontmatter, an optional @layout, then one or more phases. Frontmatter is required — a document without it falls back to a legacy syntax mode, so new scripts should always include it.
Phases are markdown H1 headings (# followed by a space; ## and deeper are not phases). The {#id} is optional — when omitted the ID is kebab-cased from the title. A trailing @if <condition> gates the phase. Phase IDs must be unique, and content before the first heading becomes an implicit main section.
Actions
@auto action executes immediately; a bare @action pauses and creates an operator intervention. @dispatch may never be @auto.
Group | Actions |
|---|---|
Setup |
|
Communication |
|
Dispatch (never |
|
Logging |
|
Timing / resolution |
|
When @log, @sms, or @email run without @auto, the operator is shown the pre-filled message to amend or cancel before it executes.
Text, display, and interpolation
Plain text (quoted or unquoted) is shown to the operator; bold marks critical warnings and italic is secondary emphasis. Fenced code blocks embed UI components — the block type is captured and the content is passed verbatim to the frontend (conventional types: data, map, contacts, timeline, video). Variables interpolate with {{path}}; an unresolved path renders as [no <root>].
Not implemented: the {{ value | filter }} pipe syntax is not wired up end-to-end — do not use filters in production scripts.Choices and navigation
A choice target may be a phase (#id), a loop control (@continue / @break), or an inline action such as @resolve. Only @if is supported as a choice modifier. Direct navigation uses [](#id); conditional navigation uses @if / @else blocks around [](#id) links.
Control flow
- Conditionals:
@if <cond>:/@else:(no@elif). Operators:==,!=,<,<=,>,>=,in,contains, combined withand,or,not. - Loops:
@each x in list [where <cond>] [limit N]:withloop.index/loop.count/loop.first/loop.lastavailable in the body. - Wait:
@wait <event> [or <event>] [timeout(<dur>)]— pauses for known events (ai_result,call_answered,guard_arrived,police_arrived,urn_received,timeout, etc.).
Input collection
@input and @form recognize type, format, required, maxLength, and placeholder. The format value is a free-form string passed to the frontend (conventional: string, number, select, textarea, phone, …). Format-specific properties such as mask:, options:, pattern: are not parsed and are silently dropped.
UI control
@layout <name> sets the panel arrangement and must appear once, at script start. @view / @hide appear in a section preamble (before the first instruction); @toggle / @configure may appear anywhere. Layout and panel names are free-form strings and are not validated against a known set.
Context namespaces
The runtime context combines incident data from SBN with variables injected during execution:
Namespace | Contents |
|---|---|
| Alarm-queue data (installation ID, customer, address, alarm type/text, terminal, priority, zone) |
| Site details (number, name, address, phones, email, lat/long, dealer) |
| Alarm ID / panel info (type, transmitter, test mode, monitoring status) |
|
|
| Zone, signal/SIA code, site |
| 1-indexed contact map (name, phones, type, codeword, passcard, language) |
| Layer metadata ( |
| AI detection results injected at runtime |
Resolution codes
The validator warns (does not error) on unrecognized codes; at runtime the operator-facing list is fetched per dealer.
Code | Meaning | Code | Meaning |
|---|---|---|---|
| False Alarm |
| Welfare OK |
| False Alarm – Technical |
| Transported |
| Verified Alarm |
| Refused Service |
| Unable to Verify |
| Escalated |
| Cancelled |
Validation
Validation runs at compile time. Errors block deployment (@auto dispatch, dispatch-with-countdown, unknown phase reference, missing/invalid layer or version, duplicate phase ID, misplaced @layout/@view/@hide, infinite loop). Warnings flag likely mistakes (unreachable phase, no @resolve in the script, code after @resolve, unknown resolution code). Unknown action, panel, and layout names are not validated.
See also
- APSL Executor — the runtime that interprets these scripts