Skip to main content

Properties and tags

Task walkthrough: Manage properties, calendars, and tags. This page is the full configuration and troubleshooting reference.

Properties and tags both attach reusable information to your automation, but they solve different problems. A property carries a value a job reads at run time, so the same workflow can run against different paths, environments, or dates without being rebuilt. A tag carries a label you group and search by. Properties are scoped to a workspace; tags are global.

Where properties and tags are managed​

Both live in the Toolkit, on their own tab, and both are created, edited and viewed in one dialog over the list. Two things are specific to these types:

  • Properties are the one Toolkit type with no Cross Reference action. A property is named by a token in a job's own fields rather than held as a stored reference, so there is nothing for the view to resolve — and its Delete goes straight to the confirmation without a reference check. Confirm a property is unused before you remove one: a token naming a property that does not exist fails the job at dispatch.
  • Tags do have Cross Reference, and it lists the jobs carrying the tag.

Properties (workspace-scoped)​

A property is a named key/value used to parameterize jobs — for example, a path or an environment name reused across jobs.

FieldNotes
nameUnique property name. 3–255 characters, following the platform naming rules — $TempFolder and Payroll/EU are both valid.
valueThe value. May be empty — see below.
encryptedWhen true, the value is stored encrypted and is not returned when read back.
descriptionOptional.
  • Properties are workspace-scoped (default to the General workspace).
  • Don't create two properties whose names differ only in case, such as Region and REGION: both can be saved, but a token naming either one then fails to resolve, and a job using it ends Failed to initialize.
  • An encrypted property's value reads back empty by design — that is not data loss. Use encrypted properties for secrets rather than putting them in job parameters.
  • An encrypted value is never exported. A property carried in an automation bundle arrives with an empty value and has to be set again in the target environment.

An empty value, and clearing one​

An empty value is a legitimate state, and it means this optional setting is unset. A token naming such a property resolves to the empty string — it does not go unresolved and it does not fail the job.

Empty is not the same as absent, and the difference matters more than it looks:

The propertyA token naming it resolves toIf it can't resolve
exists, value emptythe empty string — -P [[OI.PASSWORD]] becomes -P n/a — it resolved
does not existnothingThe job fails at dispatch; in an event, the field keeps its [[…]] text

So delete a property you no longer want and empty a property whose setting is simply unset — they are not interchangeable, and only one of them fails loudly when you get it wrong. A value is still required to be present: a create with no value at all is rejected.

The Properties list distinguishes the states that used to look identical:

List showsMeaning
the valueA plaintext property with a value.
(empty)A plaintext property whose value is deliberately empty.
••••••An encrypted property — the value is never sent to the browser.
—The value was withheld: a deleted property.

Clearing an encrypted property's value is its own action. On the property's page, use Clear stored value — then save. (Keep stored value backs out.) The value field is disabled while a clear is pending, so it cannot show something the save would discard.

Why an encrypted property can't just be blanked

An encrypted value is never returned, so the value field loads blank. A form that submitted what it was showing would send an empty value on every ordinary save — and storing that would replace a secret that exists nowhere else. So a blank on an ordinary update is refused with an error telling you to send a new value or clear it deliberately. A blank you left alone means "keep it".

Clearing the value does not un-encrypt the property: it keeps its Encrypted flag and holds an encrypted empty value — the same state creating an encrypted property with an empty value produces.

Turning Encrypted off needs a new value

A stored encrypted value is never decrypted, so switching Encrypted off is not a way to read one back. The save needs a new value to store as plaintext in its place, and Save stays unavailable until you type one — the field explains that it wants "the value to store as plaintext". The value you type is then visible to anyone who can view the property, including in the properties list and in reports.

The stored ciphertext is left untouched by a refused attempt, so nothing is lost by trying. Through the API the same rule is a 400 naming value: encrypted: false with no value is rejected, and so is any non-boolean encrypted.

Clear stored value is not an alternative route here — a clear keeps the property encrypted, so the action is hidden while Encrypted is switched off. Turning encryption off and emptying a value are two different things, and each has its own way in.

Write access to a secret is not read access, and that is the point of the rule: before it, anyone who could edit a property could turn its flag off and read back a credential they had never been told.

Importing property values​

Import payloads carry property values in plaintext, and a value flagged encrypted: true is encrypted at rest by the import — exactly as it would be if the property were created through the API. Earlier builds stored the plaintext flagged as ciphertext, and because every read path masks an encrypted value, the corruption was invisible.

An export never carries an encrypted value; it emits an empty one in its place, expecting you to re-enter the secret before importing. So an overwriting import that would land an empty value on a property already holding an encrypted one is refused, naming the property. Supply the value it should hold, or remove it from the import. A non-overwriting import is unaffected — it is not writing to that row.

Finding a property by its value​

The Properties list shows a Value column, and you can filter on it — useful for the question "which property is set to this path?" rather than "what is this property set to?". The filter matches any part of the value and ignores case, and the term is capped at 100 characters.

Two rows are deliberately absent from the results, and both matter if you are relying on the filter to be exhaustive:

  • Encrypted properties. Their value is never shown and never searched. They are excluded from a filtered result rather than shown as non-matching, so a filter can never be used to confirm what an encrypted value is by process of elimination.
  • Deleted properties. A deleted property withholds its value whether it was encrypted or not, and the filter skips it for the same reason.
The Value column is not a place to keep a secret

A property that is not marked encrypted has its value visible in the list and searchable by anyone who can read properties in that workspace. If a value should not be read back, mark the property encrypted when you create it — the flag is what withholds the value, not obscurity.

Referencing a property from a job​

Job parameters can carry property tokens, which are resolved as the job is dispatched. Write a token as [[Name]] or {{Name}}. The events a job or workflow fires resolve tokens too, but at a different moment and under different rules — see When tokens are resolved.

Pick one delimiter per string

A single string uses one delimiter pair, not both. If the string contains [[ anywhere, only [[ … ]] tokens resolve and any {{ … }} is left exactly as written. So in [[Path]] and {{Other}}, only Path resolves. Be consistent within a value.

Computing a value instead of looking one up​

A token whose body begins with = is an expression: the platform computes the answer rather than looking a name up.

[[= [[JI.RetryCount]] + 1 ]]
[[= ToUpper([[OI.Environment]]) ]]

Arithmetic, comparisons, string slicing and case folding, date and duration maths, conversions, and nested evaluation are all available, over literals and over the properties a token resolves. Expressions are evaluated at the same three moments an ordinary token is, and a failed expression costs the same as a failed name — see Expressions for the operators, the precedence rules, and the failure behavior.

Earlier builds refused a [[= … ]] token outright. An expression that attempts an assignment is still refused, so nothing an expression computes can change a property, a threshold, or a resource count.

Scopes​

A token can name the scope it's asking about: [[Scope.Name]]. Without a scope, the name is looked up as a global property — unless it is a $ system name, which is routed to the scope that owns it instead.

ScopeWhat it reads
OIA global (workspace) property. RI is an accepted alias for the same thing.
SIThe current schedule (workflow) instance. SSI addresses the same scope. This is where a schedule instance's own properties resolve.
JIThe current job instance. SJI addresses the same scope.
MIThe agent the job is running on.
THA named threshold's current value.
RMA named resource's total units — its maximum.
RUA named resource's units currently in use.

Some scopes accept extra dot-separated parts that say which instance to read:

ScopeExtra parts accepted
SI / SSIUp to two — schedule date, schedule name
JI / SJIUp to three — schedule date, schedule name, job name
MIOne — machine name
OI, RI, TH, RU, RMNone. A stray part is a parse error, not an ignored suffix.
note
CI is a Vision-only scope, not a property scope

A Vision trigger action's values may carry [[CI.$…]] Card tokens — [[CI.$CARD NAME]], [[CI.$CARD STATUS]] and five more. The property selector offers them as a Card group when you are editing a Vision action, which is the only place they mean anything: Vision substitutes them from the firing card before the action's event is emitted, and nothing else in the platform resolves them. They are not a property scope, they do not appear in the table above, and a CI token in a job event or a notification is not substituted. See Vision triggers.

note

Addressing a different instance — another schedule, another job, or another agent, as in [[MI.$MACHINE NAME.PROD01]] — isn't supported yet and is refused rather than silently resolving to the current one.

Which MI names answer depends on what raised the message. $MACHINE NAME resolves throughout. $MACHINE OPER STATUS resolves in an agent notification about an operator mark, where the mark is the event — see what an agent notification can resolve. $MACHINE RUNNING JOBS and $MACHINE NET STATUS don't resolve anywhere yet.

System properties​

Names beginning with $ are system-managed — the platform supplies the value, and you don't create a property for them. They're written in upper case with spaces ($JOB NAME, $MASTER SCHEDULE NAME) and matched case-insensitively.

Most system names must match exactly: $JOB NAMESPACE is not a variant of $JOB NAME and won't resolve as one.

An unqualified $ name isn't a global​

Writing a system name with no scope doesn't make it one. [[$JOB NAME]] is read in the job scope, not looked up among your globals, because the platform routes a bare $ name to the scope that owns it — that is what makes the short form work at all.

A $ name the platform doesn't recognise is routed by its prefix instead:

PrefixScope it's read in
$MASTER SCHEDULE, $SCHEDULESI — the schedule instance
$MASTER JOB, $JOB, $MACHINE, $FREQUENCYJI — the job instance
caution
Don't give your own property a $ name

A global of your own called $JOB REPORT can't be read as [[$JOB REPORT]] — that token is routed to the job scope, which has no such name, so it comes back unresolved. Name it without the $, or address it explicitly as [[OI.$JOB REPORT]]. The $ prefix belongs to the platform.

When tokens are resolved​

There are four moments a token can be resolved, and they answer differently on purpose.

Where the token isResolvedIf it can't be resolved
A job parameterAs the job is dispatchedThe job fails. A parameter is an instruction to a machine, so running something nobody asked for would be worse.
An event a job or workflow firesAs the event firesThe event still fires, and that field keeps the text exactly as you authored it — tokens and all.
A notification templateAs the notification is sentThe notification still goes out, and that token renders as its own bare name. See Notification Manager.
An event raised from outside a job — the self-service portal, the API, the CLI, a webhook, or a legacy agentAs the event is processed, just before it is carried outThe event fails, naming the token. See An event raised from outside a job.

That difference matters. An event fired by a job or a notification is a message about a job, so a message body naming a property nobody defined must not take down the job it is reporting on, and one bad token in a $JOB:ADD event must not quietly stop downstream work from being scheduled. An event somebody submitted is the opposite case: nothing is already under way for it to interrupt, and the request itself is what the token was meant to configure.

Two things follow from "the message always goes out" — which is the rule for an event a job or workflow fires and for a notification, not for an event somebody submitted:

  • The unit is the field, not the message. An email whose subject resolves and whose body doesn't is sent with the resolved subject and the authored body — good substitutions aren't thrown away to punish an unrelated field. In a notification template it is finer still: a single bad token degrades on its own and every sibling substitution in the same field still resolves.
  • An unresolved value is left visibly unresolved. An event field keeps [[…]] in it, so an event you find in the event log still reads as a misconfigured token instead of looking like text somebody meant to type. A notification renders the token's name without its delimiters, which reads the same way to the person holding the page. For an expression the text inside the delimiters includes the =, so a failed [[= 1 @@ 2 ]] renders as = 1 @@ 2.
Encrypted properties are masked in a job's events, not resolved

An event a job or workflow fires never carries the value of an encrypted property. It is replaced with a mask wherever it would have appeared, including when a plain property's value references an encrypted one — so $PROPERTY:SET,TARGET,[[OI.DB_PASSWORD]] fired from a job sets the target property to the mask, not to the password.

An event submitted from the portal, the API, the CLI, a webhook or an agent is handled differently again: it is refused rather than masked, except in the one field that may carry a secret — see An event raised from outside a job.

A notification is the other way round. The same token in a notification template resolves to the real stored value and the message carries it — see Notifications. The two seams differ here, as they do on when tokens resolve and on what a failed one does.

In an event a job fires, values are pinned to the moment the event fired. If delivery is retried later, the payload built at fire time is re-used verbatim, so a clock token, a property somebody edited in between, or a live threshold or resource reading can't make the retry say something different from the original. An event submitted from outside a job is pinned the other way — the authored text is what is stored, and a retry resolves it again.

What a job or workflow event can resolve​

At event time the job's own row is the source, so tokens report what actually happened rather than what was assumed at dispatch — $JOB STATUS is the status that fired the event, and the end time, run time and termination text carry real values.

ScopeNames available
JI$JOB NAME, $JOBID, $JOBID LONG, $JOBID CMP, $JOB STATUS, $JOB TERMINATION, $JOB STARTTIME, $JOB ENDTIME, $ACTUAL RUN TIME, $MAX RUN TIME, $FREQUENCY NAME, $MACHINE NAME, and the five $ARRIVED … names on a job that matched a file
MI$MACHINE NAME — the agent that actually ran the job, not the one configured
SI$SCHEDULE NAME, $MASTER SCHEDULE NAME, $SCHEDULE ID, $SCHEDULE INST, $SCHEDULE DATE, $SCHEDULE DATEMS

A workflow event has no job in scope, so it answers the SI names plus $SKD STATUS; job-scoped names in a workflow event fall through like any other unresolved name. Anything not listed above is looked up as an ordinary property, so your own globals and instance properties work as they do elsewhere.

note
$SCHEDULE INST is the run, not the rebuild

$SCHEDULE INST reports which run of a schedule instance this is for the date — the same number the $nnnn suffix shows, 1 for anything not repeat-built. It is not the build number, which counts rebuilds of one run. See Building one instance more than once for a date.

A status notification's payload used to seed it from the build number instead, so a job's command line and a notification about the same run could report different numbers for one token. All three now answer with the run. A rebuilt first run — run 1, build 2 — therefore reports 1 in a notification where it used to report 2.

A job that never started renders N/A for its start and end times rather than leaving the token standing, and a job that finished OK renders an empty termination — those are values, not failures to resolve. $JOB STATUS CATEGORY is not available at event time and does not resolve.

The file a File Arrival job found​

A File Arrival job — Windows or UNIX — records the path it actually matched, and five JI names carry it into the events and notifications that job fires. A pattern such as /data/inbound/*.csv can match any of a thousand files; these are how a later job is told which.

For a matched /data/inbound/ledger.csv:

NameValueWhat it is
$ARRIVED FILE NAME/data/inbound/ledger.csvThe whole path, exactly as the agent reported it
$ARRIVED FILE PATH/data/inboundThe directory part
$ARRIVED SHORT FILE NAMEledger.csvThe last segment
$ARRIVED BASE FILE NAMEledgerThe last segment without its extension
$ARRIVED FILE EXTENSION.csvThe extension, leading dot included

They are JI names like any other, so [[$ARRIVED SHORT FILE NAME]] and [[JI.$ARRIVED SHORT FILE NAME]] are the same token.

An empty part is a real answer, not a failure: a path ending in a separator has no short name, and a file with no dot in its name has no extension, so those render as nothing at all.

A run that matched no file resolves none of them

A job that did not report a file — anything but a File Arrival job, a File Arrival job that failed to match, a run cleared by a restart — supplies no value for any of the five. In an event or a notification the token then renders as its own bare name, exactly as any other unresolved name does there. That is deliberate: there is no file, and a blank would read as though there were one and it had no name.

The four derived parts are worked out only for the Windows and UNIX File Arrival job types, because the split depends on which platform's path rules apply. A file reported by anything else resolves $ARRIVED FILE NAME and nothing more.

Windows paths split on either \ or /. A file at a drive root keeps the separator (C:\ledger.csv → C:\), and a UNC path keeps its share (\\srv\share\ledger.csv → \\srv\share).

An event raised from outside a job​

An event nobody's job fired — one submitted from the self-service portal, the API, the CLI, a webhook, or written into a legacy agent's MSGIN directory — resolves its [[…]] tokens too, as the event is processed. So a self-service button can carry $JOB:ADD,[[$DATE]],DAILY,MYJOB and a script on an agent can name a schedule with [[OI.REGION]], both of which used to arrive as their literal text and be refused or applied as the characters themselves.

Resolution is strict here: a token that does not resolve fails the event. A lenient render would turn a mistyped [[OI.REGON]] into the text OI.REGON, which looks like a perfectly valid value and would be applied to real work. The failure names the token as you wrote it and the field it sat in, and never a resolved value — the message is kept in the event log, where more people can read it than can read the properties it resolved.

The tokenWhat happens
A global property that existsResolves to its value.
[[$DATE]], [[$TIME]], [[$NOW]] and their formatted formsResolve from the clock.
A name no global property hasThe event fails: no global property with that name exists.
A job, schedule instance or machine scoped token (JI, SI, MI, TH, RS)The event fails. Such an event has no job in scope to answer them — and that is the point of the message, rather than a silent empty value.
A token in the event type position of a command lineRefused: the event type must be written literally, because it is what the event is routed and validated as.

The stored payload keeps the text you authored. Nothing resolved is written to the event record, so the event log shows a submitted event as the author wrote it, and a retry or replay resolves it again rather than re-using an earlier result. One consequence worth knowing: [[$DATE]] in an event retried tomorrow means tomorrow, exactly as it would if the caller had submitted it again today.

A value that would break the field it lands in is refused. An event's fields are separated by commas that cannot be escaped, so a resolved value carrying a comma into a positional field, or a ; into the properties field, fails the event with a message saying so instead of silently decoding as an extra entry. The tags field is the exception — it takes the rest of the line, so a comma there is content.

A property lookup that is temporarily unavailable is not your mistake. If the service that holds the properties cannot answer, the event fails as a temporary failure and stays replayable, so a retry can succeed. Only the event as written produces a permanent refusal.

An encrypted property may only be copied, and only into an encrypted target

In an event raised from outside a job, an encrypted property may appear in exactly one place: the value of $PROPERTY:SET or $PROPERTY:ADD, written there as a single bare [[OI.NAME]] reference with nothing else around it. Anywhere else — another field, an expression over it, text either side of it — the event is refused, with the same message every time so that nothing about the secret can be inferred from which rule caught it.

And the property it is copied into must itself be encrypted. $PROPERTY:SET refuses a plaintext target, and $PROPERTY:ADD creates the property encrypted. Otherwise $PROPERTY:ADD,TMP,[[OI.SECRET]] would decant the secret into a property anyone who can read properties can read, and [[OI.TMP]] would then pass every encrypted-value rule there is.

The copy is written as encrypted, not merely checked against an encrypted target. The target's Encrypted switch could be turned off between the check and the write — a window of a moment, but a real one, and the write would then have landed the secret in the clear. A value that came from an encrypted property is now written with its encryption asserted, so it is stored encrypted whatever the target's switch says by the time the write arrives. A plain value still leaves the target's switch alone.

An instance property can never hold one. A property written onto a workflow or job instance has no encrypted form for the value to go into, so an event copying an encrypted property onto one is refused rather than storing it in the clear.

These rules now cover a notification's OpCon Event action as well. That channel renders its own payload and submits the result, so it used to arrive past the point where the rules were applied — and a notification action reading $PROPERTY:ADD,TMP,[[OI.DB_PASSWORD]] really did create a plaintext TMP holding the decrypted password. The action now declares that the value came from an encrypted property, and the same three rules apply to it. If you have such an action configured, the plaintext property it already created still exists — the fix stops new ones being made, and cannot reach back into one that was. Delete it, or turn its Encrypted switch on and re-enter the value.

A custom date format is ignored in a field read as a date

A tenant can change how [[$DATE]] renders everywhere by defining a global named $DATE — see Date and time tokens. That is right for text a person reads and wrong for a field a parser reads: an event's scheduleDate and dates fields are read month-first, so 05/09/2026 written for 5 September would land the job on 9 May.

Those two fields therefore always render with the built-in format and ignore the override. Every other field still honours it. A suffixed form such as [[$DATE_EU]] has no built-in format to fall back on, so in a date field it is refused rather than landed on the wrong day.

This now holds on every path that produces such an event. Three of them do, and each resolves its own tokens rather than being resolved a second time on submission — so each had to be fixed separately, and for a while they disagreed:

The event is produced byHides the override
A submission from outside a jobYes
A job or workflow firing oneYes
A notification's OpCon Event actionYes

The notification channel was the last of the three, so a notification action was still putting 5 September's job on 9 May after the other two had been corrected. All three now hide the override and render the date in the default culture, because a built-in Short Date is only month-first in some cultures while the parser reading it is month-first in all of them.

A date format cannot come from an encrypted property. If the global that names the format is marked Encrypted, the token is refused rather than rendered — the date would otherwise be a function of a secret, computed with nothing recording that the secret had been read. A strict consumer refuses the event and says the format is encrypted; elsewhere the token degrades to its own name. The message names the property and never its value, and it is deliberately not the same message as a malformed format: an operator cannot open an encrypted value to hunt for a typo, so telling them to look for one would send them nowhere.

Everything in What a job or workflow event can resolve otherwise still applies to a job-fired event as before: its producer resolves the tokens and deliberately leaves some text literal, and it is not resolved a second time.

Writing a property onto a running instance​

$PROPERTY:ADD, $PROPERTY:SET and $PROPERTY:DELETE used to reach only global properties: an instance-scoped name was refused with Property scope 'SI' is not yet supported. They now write onto a workflow instance or a job instance, so a job can record what it measured onto its own run and a later job in the same run can read it back with [[JI.…]].

The target is named in the property-name field, with dot-separated parts:

NameWhat it writes to
NAME, or OI.NAMEA global property, as before
SI.NAME[.date[.workflow]]A workflow instance's property
JI.NAME[.date[.workflow[.job]]]A job instance's property
SSI.NAME[.date[.workflow]]The parent workflow instance of a sub-workflow instance
SJI.NAME[.date[.workflow]]The container job that owns a sub-workflow instance
MI.…A machine property — still refused
caution
SSI and SJI mean something different here than in a token

Read as a token, [[SSI.X]] is a synonym for [[SI.X]] and [[SJI.X]] for [[JI.X]] — see Scopes. In a $PROPERTY event they are not synonyms: they walk up from a sub-workflow instance to the parent workflow instance, or to the container job that owns it. A $PROPERTY:SET,SSI.X,… raised from a top-level workflow fails, because that workflow is not a sub-workflow instance and so has no parent to walk up to.

SJI accepts a fifth (job) part for Classic compatibility and ignores it — the container job is the target either way.

Leaving the trailing parts blank targets the run that raised the event. JI.DISK_PCT with nothing after it is the triggering job instance; SI.DISK_PCT is the triggering workflow instance. A part you skip is defaulted individually, in position, so JI.DISK_PCT..NIGHTLY leaves the date blank — defaulted from the trigger — and names the workflow NIGHTLY, with the job part left blank too. A blank job part in another workflow instance means the job with the same name as the triggering job, which is how you write the same value onto the same job in a different run.

That shorthand only works for an event a job or workflow fired, because only those carry the instance that raised them. An event submitted from the portal, the API, the CLI, a webhook or a legacy agent has no triggering instance, so it must name the workflow and the schedule date in full or the event fails with Cannot default schedule name: event has no triggering instance. A JI. name with every part blank raised from a workflow-level event fails the same way, with event was not raised by a job — a workflow completion has no job for JI. to mean.

The date part is read without reference to anyone's locale. These forms are accepted:

FormExample
YYYY-MM-DD or YYYY/MM/DD2026-09-30, 2026/09/30
M/D/YYYY — month first, one or two digits, / only9/30/2026
D-Mon-YY or D-Mon-YYYY, with an English month name abbreviated to three letters or spelled in full30-Sep-26, 30-September-2026
YYYYMMDD20260930
A five-digit OLE automation serial between 32001 and 9999846296

The D-Mon-YY forms are what make a rendered token work here: [[$SCHEDULE DATE]] renders 30-Sep-26 by default, and a name carrying one used to be refused as an invalid date. The date part and an event's own schedule-date field now read the same forms, with one difference — the month-first numeric form takes / only in a name. A property name is rendered with your date-format override, so a dash-separated 05-09-2026 from a dd-MM-yyyy override is refused rather than read as 9 May.

A two-digit year pivots at 2049: 26 is 2026, 50 is 1950. A month name in any other language is refused rather than guessed. An impossible date such as 2026-02-30 is refused rather than rolled forward, and a keyword such as CURRENT is not accepted in a name.

A dot that belongs to a name goes in double quotes. The quotes are removed after the split, so JI.P..."STEP.1" names job STEP.1. This is how you reach a multi-instance job, whose instances are named JOB.branch1: write JI.P..."JOB.branch1". A quote that is opened and never closed fails the event.

What a write onto an instance refuses​

Every refusal happens before anything is written, so a failed event changed nothing and stays safe to replay:

  • A system property (any $ name) cannot be written.
  • An encrypted job property cannot be written — and neither can a name that differs from an encrypted one only in case, so the rule matches how every read path decides what to withhold.
  • SET or DELETE of a name the instance does not have fails rather than creating it; ADD is the operation that creates one.
  • A name that fails for any reason never falls back to the global property of the same name.
  • An ambiguous sub-workflow target is refused. Nested children of a sub-workflow share its name and date, so SSI./SJI. picks among several candidates only when they are re-runs of one container, or builds under one top-level parent run. Anything else is refused rather than writing to whichever is newest.

A job instance's property is writable in any status, including while the job is running — Classic behaved the same way. The write is serialized against the operator actions and the dispatch sweep on that job, so if one of those is in flight the event reports another operation on job … is in progress, retries briefly, and then fails as a retryable failure rather than a permanent one.

Names match ignoring case, and the stored spelling is kept. Writing region onto an instance that holds Region updates Region and leaves it spelled that way, so a [[JI.Region]] token elsewhere keeps working.

A property value is never written to an audit trail, a history row or an error message. The event's state changes record the instance and the property name with the value redacted, exactly as they do for a global property event, and the job's history records that a property was written rather than what it was set to.

A stale Job Editor save cannot silently revert one of these writes

The Processes Job Editor saves a job instance's properties as a whole set. If a $PROPERTY event changed one of them since the editor was opened, the save is refused rather than writing the older set back over it. Re-open the job to pick up the current values and save again.

A machine-written property is also recorded as its own kind of history entry, not as an operator edit — so the editor's Push to workflow does not treat it as something you changed and push it into the master workflow definition.

Properties on a schedule instance​

A workflow's own properties are carried by its schedule instance, and are read with the SI scope. A workflow with Allow Multi-Instance on defines a separate property set per named instance, which is what makes one definition able to run per branch or per region — see Schedule instances.

Two rules apply to those properties whether or not the workflow uses named instances:

  • InstanceName is supplied by the platform. Every build stamps it with the instance's name, so [[SI.InstanceName]] always resolves; a workflow that defines no named instances gets Default. It is written last, so a property of your own called InstanceName cannot shadow it.
  • An Encrypted instance property's value is not carried onto the run, so [[SI.…]] cannot read it. The run's property set is stored in the clear, so the value is omitted by name rather than copied — and it can't be supplied as a build-time override either, since the same name rule drops that too. The name match ignores case, so ApiKey and apikey count as one property here.

Date and time tokens​

Seven system names are the exception — they match on their prefix, so anything you write after the base name is read as a format, an offset, or both:

$SCHEDULE DATE · $SCHEDULE DATEMS · $JOB STARTTIME · $JOB ENDTIME · $DATE · $NOW · $TIME

$SCHEDULE DATE mm/dd/yy the schedule date, in that format
$JOB STARTTIME(+2hh) two hours after the job started
$SCHEDULE DATE mm/dd/yy(-1dd) yesterday's schedule date, in that format

Longer names win over shorter ones they extend, so $SCHEDULE DATEMS(-1dd) resolves as $SCHEDULE DATEMS, not as $SCHEDULE DATE.

The format comes from a property, and the property's value is the format mask. The seven bare base names have built-in defaults:

TokenDefault format
$SCHEDULE DATEdd-mmm-yy
$SCHEDULE DATEMS##### (the date as an integer)
$JOB STARTTIME / $JOB ENDTIMEyyyy/MM/dd hh:mm:ss
$DATEShort Date
$TIMELong Time
$NOWGeneral Date

Write a suffixed name and the platform looks for a global property of that same name — offset stripped — and uses its value as the format. So to use $SCHEDULE DATE mm/dd/yy, create a global property named $SCHEDULE DATE mm/dd/yy whose value is the mask you want. If no such property exists, the job fails with "No date format is defined for property …" rather than quietly falling back to the default — because falling back would ignore the format you asked for.

One narrow exception, and only for a message: if you have overridden the format of one of the seven bare base names and the property store is briefly unreachable when a notification is sent, the date renders in that token's built-in default rather than leaving the token unresolved. A job parameter still defers instead — a machine instruction carrying a plausible but wrong date is worse than a job that waits.

Good to know

Earlier builds couldn't do this at all: a property named after a date token plus a format either failed the job or rendered the mask literally. Naming one now resolves to the base token, with the suffix read as the format.

caution
A format override is ignored in an event field that is a date

Two event fields are read by the platform as dates rather than shown to a person: the schedule date a job or schedule event names, and the dates a $CALENDAR:ADD carries. A token in either one always renders in its built-in format, whatever override you have defined.

This matters because your override and the platform's reader can disagree about which number is the month. An override of dd/mm/yyyy renders 5 September as 05/09/2026; the field is read month-first, so the event would have landed on 9 May — no error, just the wrong day. Rendering these two fields in the built-in format is what removes that whole class of silent mistake.

The rule holds whichever way the event was raised — one a job or workflow fires, and one submitted from the portal, the API, the CLI, a webhook or a legacy agent. Those two are resolved on different paths, and for a while only the submitted path ignored the overrides.

A suffixed date token in one of those fields therefore has no format available and is left unrendered, so the field is then refused as an invalid date, naming the field — rather than running against a guessed day. Use the bare token there. Every other global still resolves normally, and every other field still honours your overrides.

One seam is not yet covered: a notification sent through the OpCon Event channel still renders its date fields with your overrides. Use a bare token and a format you know the reader accepts there.

Offsets. A trailing (<signed number><units>) shifts the value. Units are matched exactly, in any case:

UnitsMeaning
yyyy, yyYears
qq, qQuarters
mm, mMonths
wk, wwWeeks
dd, dDays
hh, hHours
mi, nMinutes
ss, sSeconds
msMilliseconds
tTicks
eomEnd of month
(none)Days — (-1) is the same as (-1dd)

Note mm is months and mi is minutes — they're matched by exact equality, never by first letter, so there's no ambiguity, but they're easy to mix up when authoring.

Working-day offsets aren't supported

wd — a working-day offset, as in $SCHEDULE DATE(-1wd) — is rejected. In a job parameter the job fails with an error naming the offset; it never degrades to a calendar-day offset, because silently shifting by the wrong number of days would be worse than failing. In a notification the token renders unresolved and the message still goes out. Use a calendar to express working days.

Tags (global)​

A tag is a label for organizing and finding jobs (and other objects).

FieldNotes
nameUnique tag name. 3–255 characters, following the platform naming rules.
descriptionOptional.
  • Tags are global (shared across workspaces). A job carries a list of tag names.
  • Tags drive filtering and search; a cross-reference view shows which jobs use a given tag.

Creating a tag from a job​

A job's Tags field in the workflow job editor creates tags as well as selecting them. Type a name that matches no existing tag — the match ignores case — and + Create tag '<name>' is offered as the first row in the list. Choosing it creates the tag and applies it to the job in one step, so a tag you realise you need does not send you out to Toolkit → Tags and back.

A job's tag must name a tag that exists, which is why the tag is created first and added to the job only once it does. A name that the tag naming rules refuse is reported under the field, and the job keeps the tags it already had.

The create row appears only if you hold tags.create. Tags are a global-scope object type, so the permission is tenant-wide — it is not narrowed to the workspace the job lives in. Without it the field selects from the existing catalog and says Select tags; with it, the placeholder reads Select or type tag name.