Skip to main content

Write and manage scripts

Write scripts

Choose a language

Scripts can be written in JavaScript, TypeScript, or Groovy. Pick whichever your team already knows — teams migrating from ScriptRunner can stay in Groovy.

  • JavaScript runs in-process and is the default.
  • Groovy is available on On-Premises deployments only, through a configured external runner, and is not recommended for taskPreAction (pre-submit guards) unless its latency has been proven for your instance.

Write with the readable syntax or the full runtime

For most rules, the readable, high-level API is enough and reads close to plain language. It resolves fields, statuses, and options by their display name, so you don't write against raw IDs:

issue.field("Priority").is("Highest")
issue.update({ Priority: "Medium" })

When you need more control, you have the full runtime — the raw objects, field/option IDs, and standard language features. For example, reading the fields that changed in the triggering event:

function getChangedFields() {
const events = Array.isArray(context.taskEvents) ? context.taskEvents : []
return events.flatMap((e) => e.task_fields || [])
}

See the Reference for the complete list of runtime objects, helpers, and globals available in the sandbox.

Get help while you type

The editor offers inline help — as you type, it suggests the available methods with short descriptions (e.g. "Read and compare one issue field by display name") and flags requirements (some methods need an event, taskActionDone, or taskPreAction trigger). A Guide button opens fuller reference, and Validate / Dry Run let you check a script before publishing.

Work in the Script editor

The Script tab is your editor (IDE). It provides inline suggestions (autocomplete), shows your cursor position and language, marks unsaved work as a draft, and keeps Version History for every script. Use Validate and Dry Run to check a script; Save, then Publish to make it eligible to run; and Automation to bind it. Review and approval remain available for teams that require stricter governance, but they are not required by the default flow. Dry Run never writes data; real changes come from trigger execution, REST endpoint calls, or an administrator-confirmed Run now of a published script.

Script lifecycle actions show Create, Save, Publish, or Published according to the current state. More contains Validate, Execute history, Version history, Related automation, Duplicate, and the Disable or Delete actions allowed by the current state. The editor toolbar contains Guide, AI Skill, Dry Run, and Run now.

Run a published script once

Run now performs one live execution of a published script. It is not a Dry Run, and it does not create a Trigger Binding or change a Timer Rule.

Run now appears only when the script is Published and the server confirms that its latest published version does not depend on:

  • a current issue;
  • an event or field changes;
  • a workflow action or pre-action rejection;
  • timer scheduling context;
  • an inbound HTTP request, caller, or response.

A batch script can use issue.search(...) to select explicit targets. Eligibility is based on what the source actually needs, not on its stored or historical script type.

After clicking Run now, confirm the script name, published version, and the risk of changing live data or calling external systems. When it finishes, click View execution to open that exact execution. Result Unknown means the script may have run but the final recorded state could not be confirmed; inspect the Execute Log and business data before retrying.

Change an issue's status

Status is controlled by the issue's workflow, so it is not sent through the ordinary field-update endpoint. To move the current issue to an exact target status, use:

issue.transitTo("Done")

ONEScript looks up the issue's currently executable workflow actions and runs the single action whose target is exactly "Done". Status names and IDs are case-sensitive. If no action reaches the target, or more than one action reaches it, the script fails explicitly instead of guessing.

When the status and other fields belong to the same workflow action, use the compatible update form:

issue.update({
Status: "Done",
Resolution: "Fixed"
})

Both forms use the same workflow transition path. Most transitions need no second argument. Pass options to issue.transitTo(...) only when the workflow form requires fields, a comment, attachments, or time data; see Status transitions for the complete shape.

Dry Run may read the currently executable actions to preview the transition, but it never executes the workflow action. A taskPreAction guard also cannot start a nested workflow; use api.reject(...) there to block the action being submitted.

Use variables and secrets

The Resources → Variables tab lets you define reusable values shared across scripts — useful for configuration and for secrets you don't want to hard-code. Read a value in a script with vars.get("NAME").

  • ONEScript masks an exact Secret value in execution logs, script output, and recorded effect payloads, including Secrets created or first resolved during that execution.
  • This exact-value masking does not cover an encoded, hashed, split, partially extracted, or otherwise transformed value. Treat script authors who can read Secrets as trusted credential users.
  • Never log, return, comment, notify, or persist a Secret. Automatic masking is a last-resort safeguard, not permission to expose it.

Run scripts with triggers

A script does nothing until something runs it. Each script has a trigger that decides when it runs. ONEScript provides these eight trigger types:

TriggerWhen it runsLevel
consoleDry Run from the editor — read-only checks and diagnosticsL1 (read-only)
taskPreActionBefore a change is committed — can reject it (validation/guards)L0
taskActionDoneAfter a change is committedL1
eventOn an issue event (created/updated)L1
timerOn a scheduleL1
scriptedFieldTo compute a scripted/calculated fieldL0
scriptedFieldOptionsTo provide options for a native Selection field configured as single choice or multiple choicesL1 (read-only)
httpEndpointOn an authenticated inbound HTTP request (/hooks/<slug>, POST)L1

Levels matter. L0 triggers (taskPreAction, scriptedField) are synchronous and high-frequency, so they get no network and no storage — keep them small and fast. L1 triggers may use controlled adapters (network, storage, OpenAPI) subject to read/write rules. scriptedFieldOptions may read ONES/OpenAPI data but cannot write issues, fields, storage, variables, comments, or notifications. See the full Trigger policy matrix.

skip(...), api.reject(...), and a runtime timeout are terminal. Catching the signal in script code does not resume host helpers, network, storage, logging, responses, or recorded effects after that point.

Validate and block changes (taskPreAction)

A taskPreAction script runs before a change is saved and can call api.reject("message") to block it — the edit is stopped and the user sees the message. This is how you enforce conditional rules (e.g. "block updates while In Review"). For a guard that runs, only api.reject(...) from script code blocks a change — a script that throws an error, times out, or hits a policy limit is logged for diagnosis but does not block the transition. taskPreAction has no network and no storage; it may reject and write submitted issue fields.

Never place a Secret in an api.reject(...) reason. If an exact resolved Secret value is used accidentally, ONEScript replaces it with [REDACTED] in both the platform reject_reason and Host decision logs. This safeguard does not cover encoded, hashed, split, partially extracted, or otherwise transformed values.

Trigger Bindings can scope guards by Project and Issue Type. ONEScript resolves missing scope from the current trusted issue before selecting bindings. A proven non-match continues normally. Only when a potentially applicable scoped guard remains unresolved does ONEScript block the action with an availability reason and run zero scripts.

When creating, enabling, disabling, editing, or deleting a guard succeeds, that result is effective for the next workflow action. There is no cache-expiry wait, restart, or reinstall step.

Inside the guard, field reads report the pre-change state: issue.field("Planned end date").value is the value before this edit. To read the incoming (pending) value, use the .newValue accessor — issue.field("Planned end date").newValue — or a change helper such as changedTo(...). See Field values for the full .value / .newValue contract, and the cookbook for examples.

Require registered work before completion

Use the current worklog aggregate in a taskPreAction guard when a transition depends on registered or estimated time:

if (
issue.status.changedFromTo("In Progress", "Done") &&
!issue.worklog.hasSpent()
) {
api.reject("Please register work before completing this issue.")
}

issue.worklog provides current spent, estimated, and remaining hours, plus comparison helpers such as hasSpent(), hasEstimate(), and isOverEstimate(). See Worklog aggregate.

For a Dry Run of a worklog guard, enter the issue's visible key rather than an internal UUID:

{
"action": "transit",
"issueKey": "PROJECT-123",
"changedFieldSnapshots": [
{
"field_uuid": "field005",
"old_value": { "name": "In Progress" },
"value": { "name": "Done" }
}
]
}

ONEScript resolves the key within the current team and reads the current worklog aggregate without changing the issue.

Act after a change (taskActionDone)

Runs once a change is committed — for follow-on actions like updating a related field, commenting, or syncing to an external system.

React to events and schedule jobs

  • event reacts to issue events. Events can be delivered more than once, so make one-time effects idempotent (gate them on event.eventID + api.storage).
  • timer runs on a schedule — configured under Automation → Timer Rules.

A Timer Rule uses a fixed interval. Enter a positive integer and select Seconds, Minutes, Hours, or Days. The range is 30 seconds through 366 days, and one day is always 24 hours. Decimals are not supported; enter 1.5 hours as 90 minutes.

After creating, re-enabling, or changing the interval, the server calculates Next run as one full interval from the save time. Renaming the rule preserves the existing Next run. The rule card shows the server-provided Last run and Next run. Cron, “every day at,” natural-language schedules, and timezone calendar schedules are not supported.

Create a native Float scripted field

Float scripted fields are available in the private-full On-Premises package. Write and publish a synchronous script that returns one finite number:

return 42.5

Then open Resources → Scripted Fields, choose Float, enter the field name, select the Published script, and create and enable the rule. Do not look up or enter a field UUID: ONEScript discovers the manifest-provisioned Float field, renames it from its hidden placeholder, and keeps the UUID internal. One app installation owns one native Float field; editing the rule renames or rebinds that field rather than creating additional Float fields.

Create a native Selection scripted field

Selection scripted fields are also available in the private-full package. Write and publish an options provider, then open Resources → Scripted Fields and choose Selection, the field name, ONES object, single choice / multiple choices mode, and Published script.

For a fixed list whose labels will never be renamed, strings are the shortest form:

return selection.options.from(["High", "Medium", "Low"])

Each string is both the saved identity and display value. Renaming the string therefore creates a different option. For options that may be renamed, keep a stable business/source ID and change only the display value:

return selection.options.from([
{ id: "level-high", value: "High" },
{ id: "level-medium", value: "Medium" }
])

{ id, value } is the recommended author shape. uuid and id are equivalent identity aliases; value, name, label, and title are equivalent display aliases. They do not represent different behaviors, and the ID is not an ONES field UUID. ONEScript converts this author-friendly shape into the platform's { uuid, value } option protocol. Keep id unchanged when the label changes so previously saved values still resolve.

For dynamic issue options, use the semantic adapter rather than writing paging or UUID lookup code:

return await selection.options.fromIssues({
where: {
issueType: "Requirement",
fields: { Priority: "Highest" }
}
})

By default, the option ID is the issue UUID and the display is {key} - {title}. The helper searches key/title, pages automatically, and resolves IDs supplied by ONES with an exact current-team lookup even if a saved issue no longer matches the current filter. Customize the display with display and search fields with searchBy; see Native scripted fields.

selection.options.fromIssues(...) searches and pages according to the option request from ONES. An author can set maxItems as a business-data guard; exceeding it fails explicitly. An options script has a five-second execution limit and does not return partial options when the source is too slow or incomplete.

For a paged REST, OpenAPI, or ONESQL catalog, use selection.options.fromProvider({ search, resolve, id?, display? }). The author supplies the source-specific search and exact saved-ID lookup; ONEScript owns request parsing, pagination, deduplication, and response shaping. Both callbacks are required because ONEScript cannot infer how an external system retrieves records by ID.

Call out to APIs and external services

Outbound calls — a script calling the platform API or an external service — are supported; see Calling APIs from a script.

Expose a REST endpoint (httpEndpoint)

A REST endpoint lets an external system call the exact latest version of any Published script over HTTP — the ScriptRunner "Custom REST Endpoint" equivalent. The stored script kind does not need to be httpEndpoint; endpoint execution supplies the request, authenticated caller, and response surfaces. Create and configure it under the REST Endpoints tab: pick a Published script, a URL slug, an auth mode, and a runAs mode.

  • How it's reached. POST /hooks/<slug> (the inbound route is POST-only).
  • Read the request. The script gets context.request{ slug, method, path, query, headers, body, rawBody }. For JSON, body is the platform-parsed value and rawBody is a normalized reconstruction, not the caller's original protocol bytes. Auth and secret headers are stripped before your script sees them.
  • Send the response. Shape it with api.response.send({ status, headers, body }); otherwise the script's plain output becomes a 200 JSON body. Status must be an integer from 200 through 599. Response headers are validated and bounded; unsafe metadata or a body over 256 KB returns a safe 500 instead of being forwarded or truncated.
  • Authentication. Choose ones (a verified ONES OAuth token), token (a generated shared token), or hmac (a generated signing secret). Token/HMAC credentials live in an endpoint-reserved Secret, are shown once when generated, and can't be replaced with an ordinary script variable. Same-mode edits reuse the Secret; changing authentication rotates it; switching to ones or deleting the endpoint removes it. An ONES-authenticated caller must also be a member of the endpoint's team.
  • Sign JSON requests. New HMAC clients send X-ONEScript-Signature-Version: onescript-json-v1, X-ONEScript-Timestamp, and X-ONEScript-Signature: sha256=<hex>. Compute the HMAC-SHA256 hex digest over the UTF-8 bytes of onescript-json-v1\n<timestamp>\n<RFC 8785 canonical JSON>. Equivalent whitespace and object-key order then produce the same authenticated value. A provider that can sign only its original raw JSON bytes isn't compatible with this mode.
  • Scope. REST endpoints don't support Project or Issue Type scope. Non-empty scope configuration is rejected instead of being silently ignored.
  • Run as. Admin mode uses the app identity and requires explicit caller constraints. Caller mode is available only with ones authentication. Direct OpenAPI, generated openapi.* helpers, ONESQL, issue.search(...), issue field updates, comments, and status transitions run with the current requester's ONES identity and permissions. Insufficient permission fails explicitly, with no admin fallback. Hosted API, team-variable administration, and api.notify(...) remain unavailable.
  • Reliability and limits. Authentication, idempotency, execution, and effect commit all have finite deadlines and size limits. Pre-execution unavailability returns a safe 503 without running the script; an unknown commit returns a safe 500 and keeps the idempotency key rather than risking a duplicate.

See the Reference for the request/response shape and auth detail.


Manage and monitor scripts

ONEScript gives you visibility into what scripts did — across several surfaces, for several audiences.

Inspect executions (Execute Log)

The Execute Log records each run: whether the script ran or was skipped, the outcome, and the event that triggered it. api.log(...) writes to this log, including on the skipped/no-op path. This is the author's first stop for debugging. Each entry identifies the exact script and version that ran, plus the raw workflow/event context.

Use More → Execute history in a script to open the Execute Log filtered to that script. Use Execute history on a Timer Rule card to open records filtered to that rule. After Run now, View execution opens that exact execution. Select a record to inspect its version, status, duration, operations, output, and errors.

What a failure message can and can't tell you. When a platform or third-party call fails, the log records the HTTP status and a fixed error classification — not the upstream response body, headers, or stack. Upstream responses are untrusted infrastructure data that can contain credentials, so ONEScript never copies them into a log you can read. Two things you do get: the query engine's own verdict on a ONESQL statement you wrote (so a syntax or field mistake is still actionable), and validation messages ONEScript itself produces, such as a blocked host or an invalid timeoutMs. If you need the upstream body while developing, read it in the script and log only the specific field you need — remembering that Secrets are masked but transformed values are not.

Trace event delivery (Event Log)

The Event Log traces event delivery and rule matching — useful when a binding doesn't seem to fire. It shows delivery, duplicate/invalid handling, which rule matched (hit/miss), and the corresponding execution IDs.

See changes in issue history

When a script changes an issue, the change appears in that issue's history attributed to ONEScript (e.g. "ONEScript updated Priority") — beside human edits. Anyone viewing the issue can see that an automated change occurred, what it was, and when.

Audit system-wide activity

Changes made by scripts are also captured in the system audit log, giving administrators and compliance teams a system-wide record of automated activity.

Compare and roll back versions

Every script keeps a full version history — you can audit, compare, and roll back to any previous version of the script source. Rollback creates a new version (it never rewrites or deletes history), and each version records who changed it and when.

Organize scripts in folders

Saved scripts live in the Script tab in a Script Manager tree — a searchable, multi-level folder structure. You can create, rename, and move folders, drag scripts between them, and bulk-move scripts. Scripts without a folder live in an Uncategorized bucket; deleting a folder rehomes its scripts there rather than deleting them.

Pause, disable, or retire a script

A script and its trigger are separate things, so you control whether a script is live from Automation — you don't have to delete the script to stop it.

  • Pause one trigger. Turn off its Trigger Binding (or Timer Rule) with the enable/disable toggle under Automation. The script stays published; it just stops firing through that binding until you toggle it back on. Quickest way to halt a single misbehaving rule.
  • Disable the script. You can also disable the script itself — it stops running everywhere it's bound, without touching individual bindings or deleting anything. Re-enable it to bring it back.
  • Retire it. When a rule is gone for good, remove its bindings and timer rules so nothing can trigger it. The script's version history is preserved, so you can re-bind or roll back later.
  • Editing isn't disabling. Editing a published script creates a new draft; the published version keeps running until you publish the new one. To change a live rule safely, edit it, Dry Run, then re-publish — the binding stays in place.

Administer ONEScript

Protect Workbench access

The ONEScript Workbench is protected by the onescript_workbench_access permission point. An explicit denial returns HTTP 403. If the permission service is unavailable, Workbench API requests return HTTP 503.

Know who scripts run as

Scripts normally act under ONEScript's application identity. The exception is an ONES-authenticated REST endpoint configured with runAs: caller: supported OpenAPI, ONESQL, issue search, field updates, comments, and status transitions run with the current requester's ONES identity and permissions, with no admin fallback. Hosted API, team-variable administration, and notifications remain unavailable. In ordinary admin-mode automation, issue history attributes changes to ONEScript (for example, "ONEScript updated …").

Deploy on On-Premises or SaaS

ONEScript runs on Private (On-Premises) today; SaaS is coming soon.