Actions (ActionDecl)
Generated from the doc schema (CatalogDocSchema, @canonify/kernel-catalog/doc): each field’s type, default and description are the schema’s own, and each example is a bundle file that readBundle decodes. A field’s Example is its value in one of those examples.
ActionDecl
An action (runtime).
The actions slot of the doc holds these, keyed by id. In a bundle each is one file, actions/<name>.json, named by its name, which the file then leaves out.
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
id |
ActionId | yes (propose assigns it; a new declaration leaves it out) | — | — | An action id (ac_…); renames keep it. |
name |
Ident | yes | — | "add_note" |
The action name. |
label |
Text | yes | — | "Add note" |
The words shown for it. |
subject |
TypeId | no | — | "ty_3f8v6h2n0c" |
The type of the row it runs on; absent = it runs on no row. |
inputs |
InputDecl[] | yes | — | (see the example) | The inputs, in field order. |
lets |
LetDecl[] | no | — | [{"name":"line_count","value":"count(lines)"}] |
Named values, evaluated in order before the preconditions. |
preconditions |
PreconditionDecl[] | yes | — | (see the example) | The conditions the call needs. |
effects |
EffectDecl[] | yes | — | (see the example) | What the call does, in order, in one transaction. |
outputs |
Record<Ident, Expr> | no | — | {"lines":"line_count"} |
The CX value of each output the call returns. |
approval |
ApprovalRule | no | — | (see the example) | The approval a call needs before it runs. |
idempotency |
"key" | "none" |
no | "key" |
"key" |
key: a repeated idempotency key replays the first answer (default); none: no key is accepted. |
confirm |
ActionDecl.confirm | no | — | (see the example) | The call runs only when the caller confirms this message. |
writeRights |
"invoker" | "action" |
no | "invoker" |
"invoker" |
Whose rights the writes need: the invoker’s (default) or the action’s own, a declared widening. |
limits |
ActionDecl.limits | no | — | {"maxRowsWritten":100} |
The action’s own limits, under the platform’s. |
presentation |
PresentationOverrides | no | — | (see the example) | The presentation keys the action overrides (pages). |
actions/add_note.json
{
"label": "Add note",
"subject": "ty_3f8v6h2n0c",
"inputs": [
{
"name": "note",
"writes": { "type": "ty_3f8v6h2n0c", "prop": "pr_4p6r8t0v2x" }
}
],
"preconditions": [],
"effects": [
{
"op": "update",
"target": "this",
"set": { "pr_4p6r8t0v2x": "input.note" }
}
],
"presentation": {
"inputs": {
"note": {
"label": "Note for the kitchen",
"help": "The kitchen reads it when it packs the order."
}
}
}
}
actions/place_order.json
{
"label": "Place order",
"subject": "ty_3f8v6h2n0c",
"inputs": [
{
"name": "delivery",
"type": { "t": "date" },
"label": "Delivery date",
"required": true
}
],
"lets": [{ "name": "line_count", "value": "count(lines)" }],
"preconditions": [
{
"id": "has_lines",
"holds": "line_count > 0",
"severity": "refuse",
"message": { "text": "Add a line before you place the order." },
"offer": true
}
],
"effects": [
{
"op": "transition",
"target": "this",
"lifecycle": "lc_2k4m6p8r0t",
"transition": "tn_4g6j8m0p2r"
}
],
"outputs": { "lines": "line_count" },
"approval": {
"when": "total > 10000",
"approvers": { "role": "admin" },
"selfApproval": "forbidden"
},
"idempotency": "key",
"confirm": {
"message": {
"text": "Place the order for {total}?",
"params": { "total": "total" }
}
},
"writeRights": "invoker",
"limits": { "maxRowsWritten": 100 }
}
ActionDecl.confirm
The call runs only when the caller confirms this message.
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
message |
MsgRef | yes | — | (see the example) | What the caller confirms. |
ActionDecl.limits
The action’s own limits, under the platform’s.
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
maxRowsWritten |
integer (greater than 0) | no | — | 100 |
The most rows one call writes; at most the op ceiling. |
InputDecl
An input of the action.
One of:
InputDecl.writes
An input that writes a property.
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
name |
Ident | yes | — | "note" |
The input name. |
writes |
PropRef | yes | — | {"type":"ty_3f8v6h2n0c","prop":"pr_4p6r8t0v2x"} |
The property it writes; its type, label and sensitivity come from there. |
required |
boolean | no | — | false |
Whether the caller must give it; absent = as the written property. |
default |
Expr | no | — | "''" |
The value when the caller gives none. |
showWhen |
Expr | no | — | "input.channel != 'web'" |
The input is shown (and accepted) only while this CX condition holds. |
requiredWhen |
Expr | no | — | "input.channel == 'phone'" |
The input is required while this CX condition holds. |
options |
Expr | no | — | "active" |
A ref input: the CX condition a target row must hold to be offered. |
An item of inputs in an action file (actions/<name>.json).
{
"name": "customer",
"writes": { "type": "ty_3f8v6h2n0c", "prop": "pr_5n1v3x7c9d" },
"required": false,
"showWhen": "input.channel != 'web'",
"requiredWhen": "input.channel == 'phone'",
"options": "active"
}
An item of inputs in an action file (actions/<name>.json).
{
"name": "note",
"writes": { "type": "ty_3f8v6h2n0c", "prop": "pr_4p6r8t0v2x" },
"default": "''"
}
InputDecl.type
An input with its own type.
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
name |
Ident | yes | — | "delivery" |
The input name. |
type |
ValueType | yes | — | {"t":"date"} |
What the input holds. |
label |
Text | yes | — | "Delivery date" |
The words shown for it. |
required |
boolean | no | false |
true |
Whether the caller must give it; default false. |
help |
Text | no | — | "Shown to the customer." |
The help text shown with the field. |
default |
Expr | no | — | "''" |
The value when the caller gives none. |
showWhen |
Expr | no | — | "input.kind == 'other'" |
The input is shown (and accepted) only while this CX condition holds. |
requiredWhen |
Expr | no | — | "input.kind == 'other'" |
The input is required while this CX condition holds. |
sensitive |
"personal" | "secret" |
no | — | "personal" |
Personal input is masked for readers without the right; a secret is never shown (authz C4). |
An item of inputs in an action file (actions/<name>.json).
{
"name": "reason",
"type": { "t": "text", "format": "long" },
"label": "Reason",
"help": "Shown to the customer.",
"required": false,
"default": "''",
"showWhen": "input.kind == 'other'",
"requiredWhen": "input.kind == 'other'",
"sensitive": "personal"
}
InputDecl.parts
The part rows of an owner type.
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
name |
Ident | yes | — | "lines" |
The input name. |
parts |
TypeId | yes | — | "ty_9d4r7t1x5b" |
The part type: rows owned by the subject (a Lines grid). |
max |
integer (greater than 0) | yes | — | 50 |
The most part rows one call may send. |
An item of inputs in an action file (actions/<name>.json).
{ "name": "lines", "parts": "ty_9d4r7t1x5b", "max": 50 }
InputDecl.ref
An input that names a row of a type, which the action reads (a selector); CX reads it as a ref
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
name |
Ident | yes | — | "bill_to" |
The input name. |
ref |
TypeId | yes | — | "ty_7k2m9q4z1a" |
The type of the row it names: the caller picks one row of this type (a selector the action reads). |
label |
Text | yes | — | "Bill to" |
The words shown for it. |
required |
boolean | no | false |
false |
Whether the caller must give it; default false. |
help |
Text | no | — | "The customer the invoice goes to; the order keeps its own." |
The help text shown with the field. |
default |
Expr | no | — | "customer" |
The value when the caller gives none. |
showWhen |
Expr | no | — | "input.channel != 'web'" |
The input is shown (and accepted) only while this CX condition holds. |
requiredWhen |
Expr | no | — | "input.channel == 'phone'" |
The input is required while this CX condition holds. |
options |
Expr | no | — | "active" |
A ref input: the CX condition a target row must hold to be offered. |
An item of inputs in an action file (actions/<name>.json).
{
"name": "bill_to",
"ref": "ty_7k2m9q4z1a",
"label": "Bill to",
"help": "The customer the invoice goes to; the order keeps its own.",
"required": false,
"default": "customer",
"showWhen": "input.channel != 'web'",
"requiredWhen": "input.channel == 'phone'",
"options": "active"
}
InputDecl.items
A list of records whose fields are not the properties of one part type; CX reads it as a collectioninput.lines).
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
name |
Ident | yes | — | "returns" |
The input name. |
items |
InputFieldDecl[] (at least one) | yes | — | (see the example) | The fields of each record, in column order. |
label |
Text | yes | — | "Returned lines" |
The words shown for it. |
required |
boolean | no | false |
true |
Whether the caller must give it; default false. |
max |
integer (greater than 0) | yes | — | 50 |
The most records one call may send. |
An item of inputs in an action file (actions/<name>.json).
{
"name": "returns",
"items": [
{
"name": "line",
"ref": "ty_9d4r7t1x5b",
"label": "Line",
"required": true
},
{
"name": "qty",
"type": { "t": "integer" },
"label": "Returned",
"required": true
}
],
"label": "Returned lines",
"required": true,
"max": 50
}
InputFieldDecl
A field of each record of an input list.
One of:
InputFieldDecl.type
A field with its own type.
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
name |
Ident | yes | — | "qty" |
The field name; CX reads it as l.<name> of each record. |
type |
ValueType | yes | — | {"t":"integer"} |
What the input holds. |
label |
Text | yes | — | "Returned" |
The words shown for it. |
required |
boolean | no | false |
true |
Whether the caller must give it; default false. |
An item of items in an input list of records (an item of inputs in an action file).
{
"name": "reason",
"type": { "t": "enum", "values": [{ "code": "damaged", "label": "Damaged" }] },
"label": "Reason",
"required": false
}
InputFieldDecl.ref
A field that names a row of a type.
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
name |
Ident | yes | — | "line" |
The field name; CX reads it as l.<name> of each record. |
ref |
TypeId | yes | — | "ty_9d4r7t1x5b" |
The type of the row it names: the caller picks one row of this type (a selector the action reads). |
label |
Text | yes | — | "Line" |
The words shown for it. |
required |
boolean | no | false |
true |
Whether the caller must give it; default false. |
An item of items in an input list of records (an item of inputs in an action file).
{ "name": "line", "ref": "ty_9d4r7t1x5b", "label": "Line", "required": true }
LetDecl
A named value of the action.
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
name |
Ident | yes | — | "line_count" |
The name everything after it reads. |
value |
Expr | yes | — | "count(lines)" |
The CX value. |
rights |
Elevated | no | — | (see the example) | A widened read: the value is one aggregate over rows the invoker may not read. |
An item of lets in an action file.
{ "name": "line_count", "value": "count(lines)" }
PreconditionDecl
A precondition of the action.
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
id |
Ident | yes | — | "has_lines" |
The precondition name: refusals and on-fail triggers name it. |
holds |
Expr | yes | — | "line_count > 0" |
The CX condition that must hold. |
severity |
"refuse" | "warn" |
yes | — | "refuse" |
refuse stops the call; warn needs the caller to confirm. |
message |
MsgRef | yes | — | {"text":"Add a line before you place the order."} |
What the refusal says. |
offer |
boolean | yes | — | true |
Whether it also decides if the action is offered on a row; then it may not read input. |
reveals |
true |
no | — | true |
The refusal may carry the values it compared (disclosable ones only). |
An item of preconditions in an action file.
{
"id": "within_limit",
"holds": "total <= customer.credit_limit",
"severity": "warn",
"message": {
"text": "The total passes the credit limit of {limit}.",
"params": { "limit": "customer.credit_limit" }
},
"offer": false,
"reveals": true
}
ItemPreconditionDecl
A condition each item of the forEach needs before the body runs for it.
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
id |
Ident | yes | — | "positive_amount" |
The precondition name: refusals and on-fail triggers name it. |
holds |
Expr | yes | — | "item.amount > 0" |
The CX condition that must hold. |
severity |
"refuse" |
yes | — | "refuse" |
It stops the call (the only value: it is checked while the effects run). |
message |
MsgRef | yes | — | (see the example) | What the refusal says. |
reveals |
true |
no | — | true |
The refusal may carry the values it compared (disclosable ones only). |
An item of preconditions in a forEach effect of an action file.
{
"id": "positive_amount",
"holds": "item.amount > 0",
"severity": "refuse",
"message": {
"text": "Each line needs an amount above zero, not {amount}.",
"params": { "amount": "item.amount" }
},
"reveals": true
}
ApprovalRule
The approval a call needs before it runs.
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
when |
Expr | no | — | "total > 10000" |
The call needs approval only while this CX condition holds; absent = always. |
approvers |
Subject | yes | — | {"role":"admin"} |
Who may approve: a role, a group or a member. |
selfApproval |
"forbidden" |
yes | — | "forbidden" |
The requester may never approve their own call (the only value, R14). |
expiresAfter |
integer (greater than 0) | no | — | 604800000 |
How long a pending request waits, in milliseconds (C18); absent = 30 days. |
The approval of an action file.
{
"when": "total > 10000",
"approvers": { "role": "admin" },
"selfApproval": "forbidden",
"expiresAfter": 604800000
}
Elevated
A widened read: one aggregate over rows the invoker may not read (authz Elevated).
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
discloses |
Elevated.discloses | yes | — | "count" |
The one aggregate the value may be: exists, count, or a sum, min or max of a property. |
reason |
MsgRef | yes | — | {"text":"Shows how many open orders exist."} |
Why the read is widened, shown in policies.effective. |
The rights of an action’s let.
{
"discloses": "count",
"reason": { "text": "Shows how many open orders exist." }
}
The rights of an action’s let.
{
"discloses": { "sum": { "type": "ty_3f8v6h2n0c", "prop": "pr_8b2f4h6j0k" } },
"reason": { "text": "Shows the customer’s open balance." }
}
The rights of an action’s let.
{
"discloses": { "min": { "type": "ty_6c8e0g2j4m", "prop": "pr_3a5c7e9g1j" } },
"reason": { "text": "Shows when the room is next taken." }
}
The rights of an action’s let.
{
"discloses": { "max": { "type": "ty_6c8e0g2j4m", "prop": "pr_9t1v3x5z7b" } },
"reason": { "text": "Shows when the room is free again." }
}
Elevated.discloses
The one aggregate the value may be: exists, count, or a sum, min or max of a property.
One of:
- Elevated.discloses.sum
- Elevated.discloses.min
- Elevated.discloses.max
"exists"|"count"
Elevated.discloses.sum
The sum of a property.
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
sum |
PropRef | yes | — | {"type":"ty_3f8v6h2n0c","prop":"pr_8b2f4h6j0k"} |
The property it sums. |
Elevated.discloses.min
The least value of a property.
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
min |
PropRef | yes | — | {"type":"ty_6c8e0g2j4m","prop":"pr_3a5c7e9g1j"} |
The property it takes the least of. |
Elevated.discloses.max
The greatest value of a property.
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
max |
PropRef | yes | — | {"type":"ty_6c8e0g2j4m","prop":"pr_9t1v3x5z7b"} |
The property it takes the greatest of. |