Skip to main content

Flow Parameters

Every tool step in a Business Flow receives its parameters from a combination of sources: collected user input, flow constants, literal values, and runtime-injected context. Understanding where each value comes from — and which sources are trusted — is essential for building correct and secure Marketplace Apps.

Parameter sources

SourceExampleAuthorityDescription
User inputdate, property_titleConversationalCollected via ask steps from the user's messages. The LLM extracts values from natural language.
Flow variablepractitioner_nameFlow stateA value stored in the flow's variable context — may come from a previous tool step's output or an ask step.
input_mapfilter.status: $literal:openMappingMaps flow variables to tool parameter names. Supports dotted keys for nested objects.
Constantstatus: scheduledTrusted metadataDeclared in the flow's constants block. Immutable per flow definition.
Runtime contextworkspace_id, person_idTrusted runtimeInjected automatically by the runtime. Cannot be overridden by user input or LLM.
Customer identityperson_idTrusted identityThe authenticated Customer Hub Person. Injected from the session's verified identity.

input_map

The input_map block on a tool step maps tool parameter names to values from the flow's variable context.

Basic mapping

- id: run
type: tool
tool: entity.viewing.create
execution: runtime
input_map:
property_title: property_title # flow var → tool param
date: date
time: time
notes: notes

Each key is a tool parameter name. Each value is a reference to a flow variable. The runtime reads the variable and passes it to the tool.

Dotted key nesting

Keys with dots create nested JSON objects. This is essential for entity list operations that accept filter objects.

input_map:
filter.property_type: property_type # → { "filter": { "property_type": "apartment" } }
filter.city: city # → { "filter": { "city": "Madurai" } }
limit: $literal:20

Produces:

{
"filter": {
"property_type": "apartment",
"city": "Madurai"
},
"limit": 20
}

$literal: prefix

Hard-codes a value regardless of flow variables.

input_map:
filter.status: $literal:open # always "open"
limit: $literal:20 # always 20
status: $literal:cancelled # always "cancelled"

Use $literal: for values that should never change between flow executions — fixed filters, default limits, forced status values.

$constants. prefix

References a value from the flow-level constants block.

# Flow-level constants
constants:
default_status: scheduled
max_results: 50

# Tool step input_map
input_map:
status: $constants.default_status # → "scheduled"
limit: $constants.max_results # → 50

If a $constants. reference does not match any declared constant, the parameter is omitted (not passed as null or empty).

No input_map

When a tool step has no input_map, the entire flow variable context is passed through as the tool's parameters. This is rarely used in practice — explicit input_map is preferred for clarity and security.

constants

The constants block declares trusted values that are part of the flow definition. They are immutable metadata — the same for every execution of this flow version.

id: create-viewing
name: Request viewing
constants:
default_status: scheduled
auto_confirm: true
steps:
- id: run
type: tool
tool: entity.viewing.create
execution: runtime
input_map:
property_title: property_title
date: date
status: $constants.default_status

Why constants matter

Constants are trusted metadata. Unlike user input (which the LLM extracts from natural language), constants are:

  1. Declared in the signed, published flow definition
  2. Identical for every execution of this flow version
  3. Cannot be overridden by user messages or LLM-generated parameters
  4. Verified by checksum at install time

Use constants for values that should be the same every time the flow runs: default statuses, fixed limits, feature flags.

Step-level constants

Individual steps can also declare constants that override flow-level values for that step only:

- id: run
type: tool
tool: entity.property.list
execution: runtime
constants:
expand: traveler,travel_package # trusted relation expansion
input_map:
filter.status: $literal:active

Variable resolution order

When the runtime resolves a value reference (e.g., property_title in an input_map), it searches the flow's variable context. Variables are seeded from multiple sources in priority order:

1. Current flow state (active execution's collected variables)

2. Conversation memory (values from earlier in this conversation)

3. Customer profile (person_id, phone, email from Customer Hub)

4. Knowledge base (documents, FAQs — for RAG-powered flows)

5. Ask the user (only when no other source has a value)

Collected vs inferred

The runtime tracks which variables were explicitly collected from the user (via ask steps) versus inferred from memory or context. This distinction matters for ask step auto-skip:

  • If a variable was explicitly collected with a usable value, the ask step is skipped.
  • If a variable was inferred from memory but not explicitly confirmed, the ask step may still prompt for confirmation depending on the flow's design.

Template interpolation

message steps support {{ variable }} interpolation from the flow's variable context.

- id: confirm
type: message
message: Appointment booked for {{patient_name}} with {{practitioner_name}} on {{date}} at {{time}}.

Templates support dotted paths:

message: "Order {{order.code}} status: {{order.status}}"

Special template variables:

VariableDescription
{{items_text}}Formatted list of records from a previous tool step's output
{{variable}}Any flow variable by name
{{entity.field}}Dotted path into an entity object

Trust boundary

The most important concept in flow parameters is the trust boundary between untrusted user input and trusted metadata.

Untrusted (user/LLM input)

Values collected via ask steps or extracted from user messages by the LLM are untrusted. They represent what the user said, not what the system knows to be true.

# These come from user input — they are untrusted
input_map:
property_title: property_title # user said this
date: date # user said this
amount: amount # user said this

Trusted (runtime-injected)

The runtime injects values from authenticated, verified sources. These cannot be overridden by user input or LLM-generated parameters.

Trusted valueSourceInjected by
tenant_idAuthenticated sessionRuntime
workspace_idInstallation contextRuntime
installation_idSolution installationRuntime
person_idCustomer Hub identityRuntime
Flow constantsSigned flow definitionRuntime

Authority stripping

The runtime strips authority-bearing fields from caller-supplied parameters before executing any entity operation. Even if the LLM generates a person_id or workspace_id value, it is discarded. Identity comes from the authenticated context only.

Fields stripped from user/LLM input:

  • workspace_id
  • tenant_id
  • installation_id
  • organization_id
  • customer_id
  • person_id
  • context

Relation expansion trust

Relation expansion (expand) is trusted metadata. The runtime only honors expand directives declared in the flow's constants block. Any expand parameter from user/LLM input is stripped before the storage operation executes.

# CORRECT — expansion is trusted metadata
- id: run
type: tool
tool: entity.booking.get
execution: runtime
constants:
expand: traveler,travel_package # trusted
input_map:
id: id # from user input

# WRONG — expansion from user input (stripped by runtime)
input_map:
id: id
expand: traveler,travel_package # this would be stripped

Complete example

This example from Clinic Pro demonstrates all parameter sources:

id: book-appointment
name: Book appointment
description: Customer/staff book an appointment via EntityService
surfaces:
- customer
trigger:
type: conversation

steps:
# 1. Collect patient name (user input)
- id: ask_patient_name
type: ask
field: patient_name
message: Who is the appointment for? Reply with the patient name.

# 2. List practitioners (tool step with $literal)
- id: list_practitioners
type: tool
tool: entity.practitioner.list
execution: runtime
output: practitioners # store result for next step
input_map:
limit: $literal:20 # trusted literal

# 3. Choose doctor (ask with dynamic choices from tool output)
- id: ask_doctor
type: ask
field: practitioner_name
message: Which doctor would you like?
choices_from: practitioners # dynamic choices from tool output
title_field: name
value_field: name
chip_prefix: doctor

# 4. List available slots (tool step with filter)
- id: list_slots
type: tool
tool: entity.appointment_slot.list
execution: runtime
output: slots
input_map:
filter.status: $literal:open # trusted literal
filter.practitioner_name: practitioner_name # from flow variable
limit: $literal:20

# 5. Choose time slot
- id: ask_slot
type: ask
field: time
message: Pick an open slot for that doctor.
choices_from: slots
title_field: start_at
value_field: start_at
chip_prefix: time

# 6. Collect reason (user input)
- id: ask_reason
type: ask
field: reason
message: Reason for the visit?

# 7. Create appointment (all user input + auto-injected person_id)
- id: run
type: tool
tool: entity.appointment.create
execution: runtime
input_map:
patient_name: patient_name # from ask step
practitioner_name: practitioner_name # from choices
date: date # from conversation memory
time: time # from ask step
reason: reason # from ask step
notes: notes # from conversation memory
# person_id is auto-injected by runtime (scope: customer)

# 8. Confirm (template interpolation)
- id: confirm
type: message
message: Appointment booked for {{patient_name}} with {{practitioner_name}} on {{date}} at {{time}}.

# 9. Complete
- id: done
type: complete