Skip to content

DSL reference ​

The quick lookup surface: one line and a minimal snippet per construct. For rules, edge cases and worked examples, follow the links into the specification.

ConstructWhat it gives you
entitiestables + CRUD UI + a generated data layer & API
field / relation attributesuniqueness, layout, read-only, dropdown filtering, cascades
entities.uniquea business key spanning more than one field or to-one relation
renamedFroma field's former name: the stored column is renamed in place, so the values move with the new name
entities.droppedformer field / to-one relation names whose columns are removed, data included - a field merely deleted from the file keeps its column
visibleToan allow-list of roles that may read one field, enforced on the wire
patternan input-format regular expression enforced in the UI and server-side
defaultValuea field default: column default, satisfies required, and seeds a new row in the UI
dependsOnlink a dropdown to another, copy a value from the referenced record, or default a line from the open document header
inlineCreatefalse keeps a to-one picker to existing rows - no create-from-the-picker
functionan explicit presentation role (Document, Setting, ...)
labela stored, read-only display name for lookups
label / countryLabels (field)what a field is called, and the variants the tenant's country resolves
numbera platform-numbered, gap-free document field
checkscross-field / cross-line validations
checks: severity: warn / duplicate / itemsComparea check the person saving confirms instead of a refusal
checks: kind: guarda precondition over an aggregate: block, mark for a task, or reject
immutableWhen / immutablereject user writes in a status / append-only
lifecyclethe whole legal status graph, enforced on every status write
phasesthe enrichment moments an entity announces, so a silent write has a channel a consumer can bind
locksWithMastera child collection that stays writable while its master is locked
historya shadow, append-only trail of every write: property, old and new value, who, when, user or system
hierarchy / leafOnlytree entities, leaf-only references
calculated fieldsserver + UI-evaluated expressions, date helpers, call-outs
relations / compositionassociations and master-detail compositions
relations / pickablewhich target rows a to-one's picker offers: a failing row listed disabled with a message, or left out
relations / whenMasterDeletedwhether deleting a master deletes the composition children it owns, or is refused while they exist
usesreuse entities owned by another intent model
relateda read-only register of the records referencing this entity, on its own page
processesworkflows: user tasks, decisions, waits, boundary timers
task assignmentroute a user task to a role, the record owner, or a relation walk
abortOncancel the running instance when the document reaches a terminal status
retry / onErrora declared retry cycle and an error route for a calling step, with {error} recording the final attempt's message on the record
function: Attachment / Snapshota Files panel / immutable versioned printed copies
a print placeholder's alternative pathsseveral paths in one placeholder - the first non-blank one is rendered
formstask data-entry pages
actionsdeveloper-defined buttons opening custom pages
viewan additional calendar / range page, or a slot-booking page
documentItemsLayout: chatrender a document's items as a chat thread
reportsaggregations, charts, dashboard KPI tiles, balance and ledger reports, financial and cash-flow statements, user-set parameters
scopewhich lifecycle rows an aggregating report counts
widgetscustom KPI / embedded-page dashboard tiles
notify.forEachfan the block out over a related collection: one message per row, every bare path resolved against the row
attach: recordPrintin a fan-out: attach the ANCHOR record's document, rendered once, to every recipient ({record.<field>} addresses that record)
payloadthe declared envelope an outward-facing message carries (integrations and outbound alike), instead of the record as stored
the event axiswhat a reacting glue entry binds to: an entity lifecycle event, a declared enrichment phase, or a process step reached / completed
notificationsemail on an event of the axis
notify link placeholders{recordUrl} / {inboxUrl} / {appUrl} - a message that carries the way back into the application
the notify block / attach: printsend a message about a record - with the record's own document attached - from a process step, a transition or a schedule
fileNamethe name a rendered document is stored or attached under - a pattern over the record's own data
schedulescron: notify or generate records per matching row; where values may be moments relative to the firing
integrationsoutbound HTTP on a data change
inboundrecords arriving from outside: a webhook, a queue/topic message, a dropped file
inbound.accept / inbound.mapread an arrival as an envelope: gate on its type and version, map its keys onto the record, resolve a business key to a relation
inbound.map collectionsan envelope array becomes the composition children of the created record, stored with it as one unit
importsan uploaded file (CSV with locale rules, MT940, camt.053) read into rows, whole or not at all, refused when it repeats a duplicate key
outbounda record emitted on a queue or a topic when an event fires
exportsa fixed-width, delimited or XML file over an entity or a report, every run recorded on a ledger entity
rollupscounts, sums, balance + status maintenance
settlementsauto-allocation of payments across open invoices
expansionsgenerated child rows per day / week / month
generatesone-click document-from-document cloning
generates.items.wherewhich of the source document's rows become target lines - and what an unqualified one costs
generates.prompt / generates.linkask for what the source cannot supply (defaults read off the source), and write the source's link row to the new target in the same unit
generates.eventmint the document on a source event - a status write, a create, or a process step
generates.event.modeone target per source (once, default) or one per delivered event (append)
reversalsa manual Reverse on a posted document - its negated twin, linked both ways
resolvesfill a to-one from the register row valid on the record's date
transitionsguarded on-demand status flips (void / cancel / reopen)
postingsdeclarative source-document to balanced-document posting - on a status transition, on create for a lifecycle-less source, or on a declared enrichment phase
aggregateskeyed cross-entity totals materialised into their own entity
postsderived ledger rows emitted idempotently on an event
personal / partnerper-user and per-partner row-scoped surfaces
seedsinitial data, CSV-backed sets, translations
multilingual / languagestranslation tables + read-time translation overlay, on entity reads and report columns alike
translatable: falsekeeps a key of a multilingual entity out of the translation table, so what a rule matches on cannot be translated out from under it
permissionsroles

Snippets ​

entities ​

yaml
entities:
  - name: Member
    icon: user
    group: master-data
    audit: true
    fields:
      - { name: id,   type: integer, primaryKey: true, generated: true }
      - { name: name, type: string,  required: true, length: 200 }
    relations:
      - { name: loans, kind: oneToMany, to: Loan }

field / relation attributes ​

yaml
- { name: code,    type: string, unique: true, length: 30 }
- { name: total,   type: decimal, precision: 18, scale: 2, readOnly: true }
- { name: period,  type: month }
- { name: Number,  type: string, number: { series: Sales Invoice, per: Company, stampOn: create } }
- { name: Status,  kind: manyToOne, to: OrderStatus, function: EntityStatus, init: 1 }
- { name: City,    kind: manyToOne, to: City, dependsOn: { relation: Country, filterBy: Country } }
- { name: Product, kind: manyToOne, to: Product, where: { Type: 1 } }
- { name: Customer, kind: manyToOne, to: Customer, pickable: { when: [registrationNumber != null, address != null], message: "Registration data incomplete" } }
- { name: tags,    kind: manyToMany, to: Tag, through: OrderTag }   # link entity, named

function ​

yaml
- name: ProjectTimesheet
  function: Document           # header + line items + status pill + totals
- name: EmployeeTimesheet
  function: DocumentItem

checks ​

yaml
- name: JournalEntry
  checks:
    - { kind: itemsMin,      count: 1, status: 2, message: "An entry needs at least one line" }
    - { kind: itemsSumEqual, over: [debit, credit], status: 2, message: "Debits must equal credits" }
- name: JournalEntryItem
  checks:
    - { kind: exactlyOne, fields: [debit, credit], message: "Exactly one of debit / credit" }
- name: SalesInvoice
  checks:
    - { kind: compare, field: due, op: ge, than: date, message: "Due cannot be before the invoice date" }
    - { kind: requiredWhen, field: Customer.email, when: "sentMethod == 1", status: SENT,
        message: "Sent Method is E-mail but the customer has no e-mail address" }

processes ​

yaml
processes:
  - name: OrderApproval
    trigger: { onCreate: Order }
    steps:
      - { name: review,   kind: userTask,    args: { assignee: manager, form: ApproveOrder } }
      - { name: decide,   kind: decision,    args: { if: "action == 'approve'", then: activate, else: cancel } }
      - { name: activate, kind: serviceTask, args: { setRelationField: Status, value: 2, next: end } }
      - { name: cancel,   kind: serviceTask, args: { setRelationField: Status, value: 3, next: end } }
      - { name: end,      kind: end }

A calling step (a delegate: handler or a notify: send) may declare what happens when the call fails:

yaml
- name: notifyOwner
  kind: serviceTask
  args:
    notify: { to: owner.email, subject: "...", body: "..." }
    retry: { count: 3, every: PT30S }   # three FURTHER attempts, 30s apart
    onError: recordFailure              # the exhausted failure routes here
    next: provisioned
- { name: recordFailure, kind: serviceTask, args: { setField: failureMessage, value: "{error}", next: failed } }

reports ​

yaml
reports:
  - name: OrdersByMonth
    source: Order
    dimensions: ["month(orderDate)"]
    measures: ["count(*)", "sum(total)"]
    filter: "total > 0"
    scope: live                    # all | draft | live | cancelled | void
    parameters:                    # user-set inputs bound into the WHERE
      - { name: fromDate, target: orderDate, op: ge }
      - { name: minTotal, target: total, op: ge, initial: "0" }
    chart: bar
    widget: { value: "sum(total)", at: { "month(orderDate)": now }, label: Revenue (this month) }

generates / transitions / reversals ​

yaml
generates:
  - { name: invoice-from-order, from: Order, to: Invoice, map: { Customer: Customer }, sourceStatus: 3 }
  # ...or minted with no click at all, at most once per source:
  - name: delivery-from-order
    from: Order
    to: Delivery
    event: { onTransition: Order, when: "Status == CONFIRMED" }
    map: { Order: id }

transitions:
  - { name: VoidInvoice, forEntity: Invoice, from: [ISSUED, SENT], setStatus: VOIDED, when: "Paid == 0", label: Void, icon: ban }

reversals:
  - { name: reverseEntry, forEntity: JournalEntry, when: "Status == POSTED", link: Storno, backLink: ReversedBy, negate: [debit, credit] }

seeds ​

yaml
seeds:
  - name: statuses
    entity: OrderStatus
    rows:
      - { id: 1, name: DRAFT, stage: draft }
      - { id: 2, name: POSTED, stage: live }
  - name: countries
    entity: Country
    file: data/countries.csv

Planned — recognised but not yet implemented ​

The following are parsed (or reserved) but not yet materialised by a generator; a conforming tool rejects or ignores them with a clear message rather than failing obscurely:

  • Reserved function values for upcoming presentations (Board, Gantt, Timeline).
  • Cross-model status names and stage scopes — a nomenclature owned by another model is seeded there, so its stages and names cannot be resolved from the referencing file; such references are rejected with the numeric-id fallback named.
  • Bridge fields on a generated manyToMany link — the materialised link entity carries only its key and the two foreign keys; a link with data of its own is authored as an explicit intermediate entity.

Released under the Apache License 2.0.