Attributes

Attributes connect Steps and control how input values are selected.

Values Shared by Steps

An Attribute is a named value in Flow state. A Step consumes Attributes as inputs and contributes new values as outputs. Matching names create the dependency between a producer and every consumer that needs its value.

  • Inputs are the values a Step consumes.
  • Outputs are the values it contributes to the Flow.

Attribute Roles

Each Attribute has a role that defines how it’s used:

Required Input

The Step cannot execute until this input’s Collection Policy is satisfied. Values come from the Flow’s initial state or from upstream Steps: earlier Steps that produce values used by it. The none policy instead requires confirmed absence and supplies no value.

Optional Input

The Step can execute without this value. Optional inputs can have a default value and a deadline (milliseconds) that limits how long collection can remain inactive. If the Collection Policy is still unsatisfied when the window expires, the Step uses the default, or omits the input when no default is declared.

Constant

A fixed value built into the Step definition, sent as input on every execution. Useful for configuration that belongs to the Step rather than the Flow:

{
  "region": {
    "role": "const",
    "type": "string",
    "const": { "value": "\"us-east-1\"" }
  }
}

Metadata

A value drawn from execution metadata at invocation time. The key selects a field such as flow_id, step_id, receipt_token, or webhook_url. Argyll resolves it separately for each Work Item and sends it as a Step-local input:

{
  "callback_url": {
    "role": "meta",
    "meta": { "key": "webhook_url" }
  }
}

Output

The Step may produce this Attribute value for downstream Steps that declare it as an input. Successful executions may omit declared outputs. Collection uses only values actually supplied.

Attribute Types

Attributes can be any JSON type:

  • Strings
  • Numbers
  • Objects
  • Arrays
  • Booleans
  • Null

Invocation Mapping

The name used in Flow state is the Attribute’s outer name: the key in the Step’s attributes object. A mapping.name gives that Attribute a different inner name in the HTTP request or response.

This Step consumes the Flow Attribute customer_id but sends it to the endpoint as customerId:

{
  "customer_id": {
    "role": "required",
    "type": "string",
    "required": {
      "mapping": { "name": "customerId" }
    }
  }
}

Input mapping changes the name and optional value transformation before invocation. Output mapping reads the endpoint’s inner name or transforms its response, then stores the result under the outer Attribute name for downstream Steps. A mapping.script supplies a Lua or JPath transformation when renaming alone is not enough.

Collection Policies

When multiple Steps can produce the same Attribute, the Collection Policy on an input controls when the Step is satisfied and what value it receives. Set it inside the input’s role-specific configuration:

{
  "price": {
    "role": "required",
    "type": "number",
    "required": { "collect": "last" }
  }
}
PolicySatisfied whenValue received
firstFirst value arrivesThat value (remaining providers removed because they are no longer needed)
lastAll providers finishThe last value produced
someAll providers finishArray of all values that arrived
allAll providers succeed and supply the AttributeArray of all values
noneAll providers finish with no valueNothing (gating on absence)

The default policy is first.

If a required input cannot satisfy its Collection Policy and no provider can change that, the Step fails. The Flow fails if its Goals are no longer reachable.

graph TD A["Step A → price: 10"] B["Step B → price: 12"] C["Step C (price, collect: last)"] A -->|price| C B -->|price| C C -->|"waits for both,
receives 12"| Done["Executes"] style Done fill:#e8f5e9

none behaves differently from the other policies: it satisfies the input when upstream Steps produce nothing. Use it to run a Step only after confirming that a value is absent. For example, proceed only if no fraud signal was raised. With no initial value and no planned providers, absence is confirmed immediately, and no external input is needed. When providers exist, Argyll waits for all of them to finish: declaring an output does not guarantee that a value will be produced.

Initial Values Are Authoritative

Naming an Attribute in the Flow’s initial state freezes it at those values, no matter which Collection Policy its consumers declare. Every consumer resolves it from the initial values alone.

An empty array is a declaration too. Without a required match predicate, it lets some and all receive an empty array and a for_each input creates zero Work Items; a required first or last input declared empty fails its Step. With a required match predicate, an empty array closes the gate for all four policies and skips the Step instead. An optional some or all input resolves to an empty array even if its match predicate rejects every initial value. Leave a name out of the initial state entirely to let providers supply it.

A Step may still run to produce other Attributes. Its outputs for names declared in the initial state are discarded, including outputs from Goal Steps. The raw outputs remain in Work Item history.

In the Flow creation UI’s basic editor, a JSON array represents the complete candidate list. Comma-separated values are also accepted: 1, 2 and [1, 2] both supply two candidates. A blank field leaves the name out of init; [] explicitly freezes it at zero candidates. Type "" to supply one empty-string candidate, or [[]] to supply one empty-array candidate. Switching between basic and JSON modes preserves these distinctions. In the JSON editor, omit an absent key or leave its value as ""; use [""] for an actual empty-string candidate.

Collection Deadline

Optional inputs can declare a deadline (milliseconds). This is an inactivity interval for that input, not a limit on the total collection time:

  1. The window opens when all of the Step’s required inputs are satisfied. Values already available are included.
  2. Each value arriving before expiry resets that input’s timer while its Collection Policy remains pending. Other optional inputs have independent timers. Provider completion without a value does not reset a timer.
  3. Collection ends immediately when the policy is satisfied or becomes impossible to satisfy.
  4. If the window expires before the policy is satisfied, the Step uses the declared default, or omits the input if no default exists. Partial results do not satisfy last, some, or all while providers are still pending.

An expired window stays closed. Late values cannot replace the default, even if another optional input keeps the Step waiting or the engine restarts. Those values remain available to other consumers. Once an optional input has its value or its default, upstream Steps that have not started are skipped unless another Step still needs their outputs. A deadline of 0, or no deadline, means no waiting: the input must already satisfy its policy when the required inputs become ready.

{
  "profile": {
    "role": "optional",
    "type": "object",
    "optional": {
      "collect": "last",
      "deadline": 2000,
      "default": "{}"
    }
  }
}

For this last input, a value at 1.5 seconds moves expiry from 2 seconds to 3.5 seconds. Another value at 3 seconds moves it to 5 seconds. If all providers finish at 4 seconds, the Step uses the last value immediately. If providers are still pending at 5 seconds, the input uses {} instead. Useful arrivals can therefore extend the total wait beyond 2 seconds.

The Step proceeds once all required inputs are satisfied and every optional input has either satisfied its policy or selected its fallback. If collection becomes impossible before expiry, the input selects its fallback immediately. For example, all cannot succeed after a provider finishes without supplying the Attribute, and none cannot succeed after any value arrives.