Skip to content

Property expansion syntax

Wirebench expands ${...} expressions in the endpoint, envelope, headers, WS-Addressing fields, attachment names and paths of every send — SOAP or REST. A property’s own value can reference another property, so expansion is recursive.

Explicit scope, ${#Scope#name} — resolves name in exactly one named scope:

${#Env#baseUrl}
${#Project#apiKey}
${#Workspace#region}
${#Global#userAgent}
${#System#HOME}
${#Sequence#token}

Shorthand, ${name} — resolves through a fixed chain of scopes, stopping at the first one that defines name:

Env → Project → Workspace → Global

System (the process environment) is never reached by the shorthand form — only ${#System#name} reads it. This keeps a plain ${name} from silently picking up an unrelated environment variable on your machine.

Scope Where it comes from Available
Env The active environment’s properties When a project or workspace has an active environment
Project The project’s own properties Always, for a project-scoped send
Workspace The workspace’s properties When the project is inside a workspace
Global Properties set once for the whole app, independent of any project Always
System The OS process environment (process.env by default) Always, only via the explicit ${#System#name} form
Sequence Values the earlier steps of a running sequence lifted from their responses, or that a request’s scripts set Only via the explicit ${#Sequence#name} form: in a sequence run, in wirebench run, and in the app’s single sends, which read the project’s session values

A Sequence value came from a server, so it follows stricter rules than the others. It is never expanded again, so ${…} inside it stays as it is. It can’t form another reference’s name: in ${${#Sequence#n}} a response would be choosing which property is read, so the reference is refused. It is always escaped for the body it lands in. And it may not supply the scheme, host or port of a URL. In the app, a single send reads the values its project’s scripts kept this session (the explorer lists them under Values); with none by that name, ${#Sequence#name} is unresolved.

The shorthand ${name} checks, in order: Env, then Project, then Workspace, then Global. The first scope that defines name wins, even if its value is an empty string. Use an explicit ${#Scope#name} form to reach a scope that a higher one is shadowing.

A value can itself contain ${...} expressions, and an expression’s inner text can too — so ${#Project#${#Env#which}} first resolves ${#Env#which}, then looks up a Project property named by that result. Expansion recurses up to a depth limit (8 by default); going past it reports the reference as too-deep and leaves it verbatim rather than looping forever. A property that refers back to itself, directly or through a chain of other properties, is caught as a cycle and left verbatim rather than expanded.

$${ is a literal ${ — the two characters before the brace are consumed by the escape, so $${name} produces the literal text ${name}, unexpanded. In a run of three or more $ before a brace, the last two and the brace form the escape and every $ before them stays as it is. So $$${x} produces the literal text $${x}, and nothing in it is expanded.

A ${ in a definition’s own text belongs to the definition, not to you. When Wirebench makes something you send from a WSDL, an OpenAPI or an AsyncAPI document, it writes every ${ it copies as $${. That covers:

  • an XSD fixed or default value, in a generated request or inserted from the form view
  • the SOAP action
  • a WSDL soap:address, and the endpoint made from it
  • an OpenAPI path, a server URL and the base URL taken from it
  • an OpenAPI example or default
  • an OAuth2 token URL, authorization URL and scope, and an API key’s name
  • an AsyncAPI server URL and channel address, a server variable’s default or enum value, and a channel parameter’s default, enum or example value
  • an AsyncAPI WebSocket binding’s query and header names and samples, and its subprotocols
  • an AsyncAPI message example, or the sample made from its payload

An AsyncAPI {name} server variable or channel parameter is filled from the document’s own default or enum value (a parameter’s example too). One with no value stays the literal text {name} and is sent that way. It never becomes the property ${name}, because that would read whatever property, secret or environment variable the name reaches and send it to a host the document chose. The import report lists each such slot, so you can replace it with a value or a property of your own.

A contract tool’s call does the same. Each ${ then goes on the wire exactly as the definition wrote it, and never reads a property, a secret, or an environment variable through ${#System#…}. A ${…} you type later still expands as usual.

Validate checks a $${ as the literal ${ it sends, so a generated fixed value passes. wirebench generate saves nothing, so it shows the definition’s text as written.

Anything made before this rule still holds the definition’s ${ unescaped. Regenerate a request to fix it. Re-import the definition to fix an interface’s endpoints, an API’s servers and base URL, or its OAuth2 settings, since regenerating a request does not change those. Or write the ${ as $${ yourself.

An AsyncAPI API imported before this rule is not repaired by Update definition…. An update rewrites a field only while it still equals what the contract generates, and the old unescaped text no longer does, so it is kept as if you had edited it. Import the document again instead.

An expression that cannot be resolved is left in the output exactly as written — Wirebench never silently drops or blanks it. Each one is reported separately (visible in validation and before a send) with a reason:

Reason When it happens
missing The name isn’t defined in the scope (or scope chain) being checked
unknown-scope An explicit ${#Foo#name} names a scope that doesn’t exist
cycle The property refers back to itself, directly or indirectly
too-deep Expansion recursed past the depth limit
malformed A ${ was never closed with a matching }

A property can be switched off without deleting it: its name appears in that scope’s disabled list instead of being removed from properties. A disabled variable is treated as absent for resolution — the shorthand form falls through to the next scope in the chain exactly as if the variable didn’t exist, and an explicit reference to it reports missing. Its value stays on disk and is restored the moment it’s re-enabled.

Global properties have no disabled list of their own in the engine; a caller that wants a disabled global excluded from resolution filters it out before expansion.

When expanding a SOAP envelope (never headers or the endpoint), Wirebench can XML-escape every substituted value — so a property containing &, < or " doesn’t corrupt the envelope. A value is escaped once, at the outermost reference, even when it was reached through several levels of nested property values.