Skip to main content

Workflows

Workflows are the automation layer of a Marketplace App. Each definition under workflows/ is declarative YAML: at install time it is registered with the runtime, which compiles it into the same BusinessFlow / FlowRunner used by SDK-advertised flows. The package never executes anything itself.

Steps: ask → tool → condition → approval / challenge → complete.

Definition (Marketplace App)

From restaurant-pro-runtime:

workflows/create-reservation.yaml
id: create-reservation
name: Create reservation
description: Book a table through Qefro Runtime (FlowRunner → RuntimeAdapter)
trigger:
type: conversation
steps:
- id: ask_covers
type: ask
field: covers
message: How many guests?
- id: ask_date
type: ask
field: date
message: Which date should we book?
- id: ask_name
type: ask
field: guest_name
message: What name should the reservation be under?
- id: create
type: tool
tool: entity.reservation.create
execution: runtime
input_map:
guest_name: guest_name
covers: covers
date: date
time: time
table_id: table_id
- id: confirm
type: message
message: Reservation booked for {{covers}} guests on {{date}}.
- id: done
type: complete
FieldTypeRequiredDescription
idstringYesWorkflow id; must appear in the manifest's flows list.
namestringYesHuman-readable label shown in the portal.
triggerobjectYesStart condition: a business event name.
stepslistYesOrdered steps executed by the runtime.

Step types

Step types map onto the runtime's flow engine — the same model as Business Flows:

StepPurpose
askCollect input from a user on a channel
toolCall a Runtime entity capability (entity.<id>.create, execution: runtime) or, for SDK-hosted / pool apps, a connector / /qefro tool
conditionBranch on payload values
delayWait a declared duration
approvalPause for an explicit portal approval
challengeRequire identity verification before continuing
messageSend a channel message
completeFinish the execution

On Marketplace Apps, tool steps use Runtime capabilities (entity.reservation.create). execution: runtime selects the RuntimeAdapter — not an SDK process.

On external integrations, the same FlowRunner uses an SDKAdapter to invoke /qefro tools. See Runtime vs SDK.

Parameters interpolate fields ({{ covers }}, {{ event.payload.* }}); there is no scripting beyond interpolation.

Step reference

ask — collect user input

Collects a value from the user on a channel (WhatsApp, widget). If the flow's variable context already has a usable value, the step is auto-skipped.

- id: ask_city
type: ask
field: city
message: Which city or area should I search in?
FieldTypeRequiredDescription
idstringYesUnique step identifier.
typeaskYesStep type.
fieldstringYesConversation slot id to collect. Must match a conversation_slots entry or a flow variable name.
messagestringYesPrompt shown to the user.
choicesstring[]NoStatic choice list rendered as buttons.
choices_fromstringNoDynamic choices from a previous tool step's output.
title_fieldstringNoField name for display text in dynamic choices.
value_fieldstringNoField name for the stored value in dynamic choices.
chip_prefixstringNoPrefix for chip UI elements (e.g., time:10:00).

Static choices (from search-properties):

- id: ask_type
type: ask
field: property_type
message: What type of property are you looking for?
choices:
- Apartment
- Villa
- Plot / Land
- Commercial

Dynamic choices (from book-appointment — choices from a tool step):

# First, fetch the data
- id: list_practitioners
type: tool
tool: entity.practitioner.list
execution: runtime
output: practitioners # store for next step
input_map:
limit: $literal:20

# Then, present as choices
- id: ask_doctor
type: ask
field: practitioner_name
message: Which doctor would you like?
choices_from: practitioners # reference to tool output
title_field: name # display field
value_field: name # stored value
chip_prefix: doctor

tool — execute an operation

Calls an entity operation or HTTP tool. This is how flows read and write data.

- id: run
type: tool
tool: entity.viewing.create
execution: runtime
input_map:
property_title: property_title
date: date
time: time
FieldTypeRequiredDescription
idstringYesUnique step identifier.
typetoolYesStep type.
toolstringYesOperation reference: entity.<name>.<op> for runtime, or tool name for HTTP.
executionstringYesruntime (EntityService) or http (ConnectorBridge).
input_mapobjectNoMaps tool parameters to flow variables. See Flow Parameters.
outputstringNoVariable name to store the tool's result for later steps.
constantsobjectNoStep-level trusted constants (e.g., expand).

List with filters (from search-properties):

- id: run
type: tool
tool: entity.property.list
execution: runtime
input_map:
filter.property_type: property_type # dotted key → nested object
filter.city: city

Create with auto-injected identity (from request-viewing):

- id: run
type: tool
tool: entity.viewing.create
execution: runtime
input_map:
property_title: property_title
date: date
time: time
# person_id is auto-injected by runtime (scope: customer)

Capture output for later steps:

- id: search
type: tool
tool: entity.travel_package.list
execution: runtime
input_map:
filter.status: $literal:active
limit: $literal:20
output: packages # store result

- id: show_results
type: message
message: |
Here are available packages:
{{packages}} # reference stored output

message — send a response

Sends a text message to the user. Supports {{ variable }} interpolation from the flow's variable context.

- id: confirm
type: message
message: Viewing scheduled for {{property_title}} on {{date}}.
FieldTypeRequiredDescription
idstringYesUnique step identifier.
typemessageYesStep type.
messagestringYesMessage template with {{ variable }} interpolation.

Template variables:

# Simple variable interpolation
message: "Appointment booked for {{patient_name}} on {{date}}."

# List results from a tool step
message: |
Here are matching properties:
{{items_text}}

# Dotted path into entity objects
message: "Order {{order.code}} status: {{order.status}}"

Image delivery: When a message step follows a tool step that returned records with type: image fields, the runtime automatically extracts and delivers images via the channel (WhatsApp media message or widget inline image).

complete — end the flow

Terminal step that marks the flow execution as complete.

- id: done
type: complete
FieldTypeRequiredDescription
idstringYesUnique step identifier.
typecompleteYesStep type.
messagestringNoOptional final message to the user.
# With a final message
- id: done
type: complete
message: "Thank you! Your request has been submitted."

# Without a message (silent completion)
- id: done
type: complete
message: ""

condition — branch on values

Evaluates a when expression and jumps to then or else steps.

- id: check_status
type: condition
when: "status == confirmed"
then: send_confirmation
else: send_pending
FieldTypeRequiredDescription
idstringYesUnique step identifier.
typeconditionYesStep type.
whenstringYesExpression to evaluate.
thenstringYesStep id to jump to if true.
elsestringNoStep id to jump to if false.

delay — wait a duration

Pauses the flow for a specified duration before continuing.

- id: wait_before_reminder
type: delay
duration_seconds: 3600 # wait 1 hour
FieldTypeRequiredDescription
idstringYesUnique step identifier.
typedelayYesStep type.
duration_secondsintegerNoWait duration in seconds.
secondsintegerNoAlternative to duration_seconds.
untilstringNoISO 8601 datetime to wait until.

approval — require portal approval

Pauses the flow until an authorized user approves or rejects in the portal. Only Owner/Admin roles can approve.

- id: request_approval
type: approval
message: "Refund of ${{amount}} requires manager approval."

challenge — identity verification

Requires the user to verify their identity before continuing. Used for sensitive operations.

- id: verify_identity
type: challenge
message: "Please verify your email to continue."

Triggers

  1. Business events — the trigger.event name is matched on the platform event bus. ui.* lifecycle events never trigger workflows; only business events do. See Events.
  2. UI triggers — a form widget can start the workflow directly (action.trigger), gated by the workflow.trigger capability and the workflow.execute permission.
  3. Manual / CLI — tenants can trigger installed workflows:
qefro workflow trigger --solution restaurant-pro-runtime --workflow create-reservation

Registration and execution

  • Executions appear in the tenant's runtime data — the executions and metrics runtime sources can display them.
  • Failures are runtime failures: retries, observability and flow-run history behave exactly like any Business Flow.
  • Upgrading the solution replaces the workflow definition; in-flight executions of the old version run to completion.

Restaurant Pro Runtime workflows

WorkflowTriggerSteps
create-reservationconversation (book a table)ask covers/date/name → entity.reservation.create → message → complete

Real Estate uses the same engine: create-viewingentity.viewing.create. See real-estate-runtime.

Guidelines

  • One workflow per business outcome; compose with condition steps instead of overlapping triggers.
  • Use approval for irreversible actions (refunds, voiding bills) — the platform enforces who can approve. See Approvals.
  • Keep delay durations business-meaningful; a reminder sent too early is noise, too late is useless.
  • Never encode tenant-specific values (URLs, phone numbers) in steps — use settings and connector configuration.