Module Authoring
Define data models with entities, attributes, methods, workflows, and triggers.
Modules are the unit of organization in Sevro. A module contains entities, workflows, and triggers. You define modules as YAML files and apply them with the CLI.
Module structure
name: my_module
entities:
- ref: entity_name
label: Entity Name
description: Optional description
attributes: [...]
methods: [...]
triggers: [...]
workflows:
- name: workflow_name
nodes: [...]
connections: {...}Entities
An entity is a type of thing in your system. Each entity has a ref (machine name), label (display name), and optional description.
entities:
- ref: contact
label: Contact
description: A person in your CRM
attributes: [...]
methods: [...]Attributes
Attributes define the data shape of an entity.
Field reference
| Field | Type | Required | Description |
|---|---|---|---|
ref | string | Yes | Machine name (snake_case) |
label | string | Yes | Display name |
description | string | No | Human-readable description |
type | string | Yes | One of: string, number, boolean, date, datetime, enum, reference |
is_required | boolean | No | Reject saves when field is missing (default: false) |
is_unique | boolean | No | Enforce uniqueness across all records of this entity (default: false) |
default_value | string | No | Default value applied on create if field is absent |
allowed_values | []string | No | For enum type — the valid values |
target_entity_ref | string | No | For reference type — the entity this points to |
on_delete | string | No | For reference type — nullify, restrict, or cascade (default: nullify) |
computed_expression | string | No | CEL expression — value is computed, read-only |
validation_expression | string | No | CEL expression — must return true or save is rejected |
Attribute types
| Type | Stored as | Example |
|---|---|---|
string | string | "hello" |
number | int64 or float64 | 42, 3.14 |
boolean | bool | true |
date | string (RFC 3339 date) | "2024-01-15" |
datetime | string (RFC 3339 datetime) | "2024-01-15T10:30:00Z" |
enum | string (must be in allowed_values) | "draft" |
reference | string (record ID) | "uuid-of-target" |
Examples
attributes:
- ref: email
label: Email
type: string
is_required: true
is_unique: true
validation_expression: "record.email.contains('@')"
- ref: status
label: Status
type: enum
allowed_values: ["draft", "published", "archived"]
default_value: "draft"
- ref: created_at
label: Created At
type: datetime
computed_expression: "now()"
- ref: author_id
label: Author
type: reference
target_entity_ref: user
on_delete: nullifyMethods
Methods define how data changes. Every mutation goes through a method — there are no direct create/update endpoints.
Field reference
| Field | Type | Required | Description |
|---|---|---|---|
ref | string | Yes | Machine name |
label | string | Yes | Display name |
is_constructor | boolean | No | If true, this method creates new records |
conditions | []string | No | CEL expressions — all must be true to proceed |
parameters | []object | No | Input parameters the caller must provide |
actions | []object | Yes | Field assignments to apply |
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
ref | string | Yes | Parameter name |
label | string | Yes | Display name |
type | string | Yes | One of: string, number, boolean, date, datetime, enum |
is_required | boolean | No | Caller must provide this value |
allowed_values | []string | No | For enum type |
Actions
| Field | Type | Description |
|---|---|---|
target | string | The attribute ref to set |
expression | string | CEL expression — the value to assign |
Examples
methods:
# Constructor — creates a new invoice
- ref: create_invoice
label: Create Invoice
is_constructor: true
parameters:
- ref: customer_name
label: Customer Name
type: string
is_required: true
- ref: amount
label: Amount
type: number
is_required: true
actions:
- target: customer_name
expression: "params.customer_name"
- target: amount
expression: "params.amount"
- target: status
expression: '"draft"'
# Method — marks an invoice as paid
- ref: mark_paid
label: Mark Paid
conditions:
- 'record.status == "draft"'
actions:
- target: status
expression: '"paid"'
- target: paid_at
expression: "now()"
# Method — cancels an invoice
- ref: cancel
label: Cancel
conditions:
- 'record.status != "paid"'
actions:
- target: status
expression: '"cancelled"'CEL context
In conditions and actions, you have access to:
record— the current record's fields (e.g.,record.status,record.amount)params— the method parameters (e.g.,params.customer_name)now()— current timestampuuid()— generate a UUID
See the CEL reference for the full expression language.
Triggers
Triggers fire workflows automatically when events happen.
triggers:
- event_type: on_create
workflow_ref: notify_new_contact
- event_type: on_update
workflow_ref: sync_contact
field_watcher: "email" # only fires when the email field changes
- event_type: on_delete
workflow_ref: cleanup_contact_dataCron triggers
triggers:
- event_type: on_create # event_type is required but ignored for cron
workflow_ref: daily_report
cron_expression: "0 9 * * *" # every day at 9amWorkflows
Workflows are DAGs (directed acyclic graphs) of nodes connected by edges. They orchestrate multi-step processes that span entities, modules, and external services.
Node types
| Type | Description | Key parameters |
|---|---|---|
invoke_method | Call a method on a record | module, entity, method, record_ref, arguments |
invoke_connection | Call an external API | connection, action, arguments |
evaluate | Evaluate CEL expressions | expressions (map of name → CEL) |
branch | Conditional routing | condition (CEL, must return bool) |
sleep | Pause for a duration | duration (e.g., "30s", "5m") |
Error handling
Each node declares an on_error policy:
on_error:
abort: true # stop the workflow (default)
# OR
skip: true # log error, continue
# OR
retry:
max_attempts: 3
backoff: exponential # constant | linear | exponential
initial_delay: 1s
max_delay: 30sExample
workflows:
- name: order_fulfillment
description: "Fulfill an order end-to-end"
input:
type: object
properties:
order_id:
type: string
nodes:
- id: validate
name: "Validate Order"
type: invoke_method
parameters:
module: commerce
entity: Order
method: validate
record_ref: "$.input.order_id"
on_error:
abort: true
- id: check_stock
name: "Check Stock"
type: invoke_method
parameters:
module: warehouse
entity: Inventory
method: check_availability
on_error:
abort: true
- id: in_stock
name: "In Stock?"
type: branch
parameters:
condition: "$nodes.check_stock.output.available == true"
on_error:
abort: true
- id: reserve
name: "Reserve Inventory"
type: invoke_method
parameters:
module: warehouse
entity: Inventory
method: reserve
on_error:
abort: true
- id: notify_oos
name: "Notify Out of Stock"
type: invoke_method
parameters:
module: commerce
entity: Order
method: mark_backordered
record_ref: "$.input.order_id"
on_error:
skip: true
connections:
validate:
main:
- node: check_stock
check_stock:
main:
- node: in_stock
in_stock:
true:
- node: reserve
false:
- node: notify_oosData flow
Nodes reference data from earlier nodes using CEL with path syntax:
$.input.<field>— workflow input$nodes.<node_id>.output.<field>— output of a previous node$nodes.<node_id>.error— error message if the node failed
Applying modules
# Create or update a module from a YAML file
sevro module apply -f module.yaml
# Export an existing module to YAML
sevro module export <module-id> -o module.yaml
# List modules
sevro module list
# Delete a module
sevro module delete <module-id>sevro module apply is idempotent — if a module with the same name exists, it updates it; otherwise it creates a new one.
Import/export
Modules can be exported and imported via the admin UI or CLI:
- CLI:
sevro module export <id> -o file.yaml→sevro module apply -f file.yaml - UI: Module list → Export dropdown (JSON or YAML) → Import button
The YAML format uses snake_case field names matching the proto definitions. Exported YAML strips server-generated fields (IDs, timestamps) so it can be re-applied cleanly.