Skip to content

Script API reference

Oktul Code Studio’s console checks a script against these TypeScript declarations and completes from them. They are taken from Code Studio’s source, so they list exactly what a run defines, and a script imports none of them. How to use them is in writing scripts.

Globals and the context

/** A request's options: query parameters and headers. */
interface CcRequestOptions {
query?: Record<string, string | number | boolean | Array<string | number | boolean> | null | undefined>;
headers?: Record<string, string | number>;
}
/** A response as the host answered it. In the sandbox a write is answered with a simulated one. */
interface CcResponse {
status: number;
statusText?: string;
headers: Record<string, string>;
body: string;
/** True when the sandbox recorded the request instead of sending it. */
simulated?: boolean;
}
/** Thrown by the verbs on a status of 400 or more. */
declare class HttpError extends Error {
readonly name: 'HttpError';
readonly status: number;
readonly method: string;
readonly target: string;
/** The response's headers, lower case: retry-after on a 429 or 503 says how long to wait. */
readonly headers: Record<string, string>;
/** The response body, parsed when it is JSON. */
readonly body: unknown;
}
/** Jira, Confluence or Assets, by the product's own REST paths. Requests run as the run's principal. */
interface CcProductClient {
get<T = any>(path: string, options?: CcRequestOptions): Promise<T>;
delete<T = any>(path: string, options?: CcRequestOptions): Promise<T>;
post<T = any>(path: string, body?: unknown, options?: CcRequestOptions): Promise<T>;
put<T = any>(path: string, body?: unknown, options?: CcRequestOptions): Promise<T>;
patch<T = any>(path: string, body?: unknown, options?: CcRequestOptions): Promise<T>;
/** The raw response; never throws on a status. */
request(request: { method?: string; path: string; query?: CcRequestOptions['query']; headers?: CcRequestOptions['headers']; body?: unknown }): Promise<CcResponse>;
}
/** Jira's REST APIs: /rest/api/3, /rest/agile/1.0, /rest/servicedeskapi. */
declare const jira: CcProductClient;
/** Confluence's REST APIs: /wiki/api/v2 and /wiki/rest/api. */
declare const confluence: CcProductClient;
/** Assets, with paths relative to the site's workspace: /object/1, /object/aql. */
declare const assets: CcProductClient;
/** Another system, through a connection an administrator set up; the connection adds its credentials. */
declare const http: {
get<T = any>(connection: string, url: string, options?: CcRequestOptions): Promise<T>;
delete<T = any>(connection: string, url: string, options?: CcRequestOptions): Promise<T>;
post<T = any>(connection: string, url: string, body?: unknown, options?: CcRequestOptions): Promise<T>;
put<T = any>(connection: string, url: string, body?: unknown, options?: CcRequestOptions): Promise<T>;
patch<T = any>(connection: string, url: string, body?: unknown, options?: CcRequestOptions): Promise<T>;
/** The raw response. With connection null, a host an administrator approved, with no credentials. */
request(request: { connection?: string | null; method?: string; url: string; query?: CcRequestOptions['query']; headers?: CcRequestOptions['headers']; body?: unknown }): Promise<CcResponse>;
};
declare const connections: {
/** The connection keys this site has. */
list(): Promise<string[]>;
};
/** JSON values kept between runs. */
declare const store: {
get<T = any>(key: string): Promise<T | null>;
set(key: string, value: unknown, options?: { ttlSeconds?: number }): Promise<void>;
delete(key: string): Promise<void>;
list(prefix?: string, cursor?: string | null): Promise<{ keys: string[]; cursor: string | null }>;
};
/** The run's log, read in Code Studio. Secret variable values are blanked out. */
declare const log: {
debug(...values: unknown[]): void;
info(...values: unknown[]): void;
warn(...values: unknown[]): void;
error(...values: unknown[]): void;
};
/** The second argument of a script's default export. */
interface ScriptContext<State = unknown> {
/** Script variables, by name. */
environment: { vars: CcVariables };
runId: string;
mode: 'sandbox' | 'production';
/** The state of the last checkpoint when this run continued from one, else null. */
resumedFrom: State | null;
/** Marks where a continuation may resume, with the state it needs. */
checkpoint(state: State): void;
[binding: string]: unknown;
}
/** Script variables. The console adds this site's own names. */
interface CcVariables {
[name: string]: string | undefined;
}```
The editor adds this site's own variable names to `CcVariables`, so
`context.environment.vars.` completes them.
Besides these, a script has the Web APIs `fetch`, `Response`, `Headers`, `URL`,
`URLSearchParams`, `TextEncoder`, `atob`, `btoa`, `setTimeout`, `clearTimeout` and
`crypto.randomUUID`, and `console.log`, `info`, `debug`, `warn` and `error`, which write the run's
log. There is no `TextDecoder`, no Node module and no npm package.
## How requests behave
| Call | Answers | On a status of 400 or more |
|---|---|---|
| `jira.get(path, options)` and the other verbs | The body, parsed when it is JSON; `null` when it is empty | Throws `HttpError` |
| `jira.request({ method, path, query, headers, body })` | `CcResponse`: `status`, `headers` (lower case), `body` as text, `simulated` | Answers it |
| `http.get(connection, url, options)` and the other verbs | As `jira`'s, from the connection's base URL with its credential | Throws `HttpError` |
| `http.request({ connection, method, url, … })` | `CcResponse`. With `connection: null`, a host an administrator approved, with no credential | Answers it |
| `fetch(url, init)` | A `Response`, from a host an administrator approved | Answers it |
- A path starts with `/`, as Atlassian's REST documentation writes it.
- `assets` paths are relative to the site's workspace: `/object/aql`, `/objectschema/list`.
- A string body is sent as text, anything else as JSON.
- In the sandbox a write answers `simulated: true` with a simulated body.
- `/rest/api/3/uiModifications` is refused to every script, because Code Studio keeps its dynamic
forms there.
## What a scripted field's script returns
A scripted field's script exports `default (workItem, context)`, which returns one work item's
value, and for a picked field `options(context)`, which returns the choices. `null` empties the
field.
| Field type | The default export returns |
|---|---|
| Text | A string of up to 255 characters, on one line |
| Paragraph | A string; each line becomes a paragraph |
| Number | A number, or a string that reads as one |
| Date | A date as `2026-10-15` |
| Date and time | An ISO 8601 string such as `2026-10-15T09:30:00Z`, or milliseconds |
| URL | An `http` or `https` address |
| Labels | A list of words with no spaces |
| Select, radio buttons | The text of one choice |
| Multi select, checkboxes | A list of choices |
| Cascading select | `{ parent, child }`, two strings |
| User picker, one or several | An account id, or a list of them |
| Group picker, one or several | A group id, or a list of them |
```ts
/** A picked field's choices: texts, or { value, label } for people and groups. */
export function options(context: ScriptContext): Array<string | { value: string; label: string }> | Promise<Array<string | { value: string; label: string }>>;
/** One work item's value, or its default on a picked field. */
export default async function (workItem: { id: string; key: string; fields: Record<string, unknown> }, context: ScriptContext): Promise<unknown>;

A value the type refuses leaves the work items as they were and becomes a line in the run’s log.

A dynamic form rule

A dynamic form’s script runs in the person’s browser, with jira.get for reading Jira as that person and nothing else.

interface FormField {
id: string;
name: string;
type: string;
/** In Jira REST API v3's shape for the field's type. */
value: unknown;
visible: boolean;
required: boolean;
readOnly: boolean;
description: string;
/** False for a field the form does not hold; every setter on it does nothing. */
present?: false;
setVisible(visible: boolean): FormField;
setHidden(hidden: boolean): FormField;
setRequired(required: boolean): FormField;
setReadOnly(readOnly: boolean): FormField;
setName(name: string): FormField;
setDescription(description: string): FormField;
setValue(value: unknown): FormField;
clear(): FormField;
/** Show only these option ids, or hide them, up to 100. */
setOptionsVisibility(optionIds: string[], visible?: boolean): FormField;
}
interface Form {
trigger: 'init' | 'change';
view: 'GIC' | 'IssueView' | 'IssueTransition' | 'GICAgentView' | 'IssueViewAgentView' | 'IssueTransitionAgentView' | 'JSMRequestCreate';
fields: FormField[];
/** The field whose change started the run, or null as the form opens. */
changed: FormField | null;
/** A field by id (customfield_10050, priority) or by name. */
field(idOrName: string): FormField;
has(idOrName: string): boolean;
}
interface FormContext {
view: Form['view'];
trigger: Form['trigger'];
projectId: string | null;
issueTypeId: string | null;
/** Null on a create form. */
issueId: string | null;
accountId: string | null;
portalId?: string | null;
requestTypeId?: string | null;
}
export default async function (form: Form, context: FormContext): Promise<void>;

Which changes run a rule. Code Studio reads the field ids a rule writes as literal strings in quotes, 'priority' or 'customfield_10050'. A rule that names any runs again only when one of those fields changes, so a field it reads by display name or by an id built at run time starts no run. A rule that names no id that way runs on every change.

When each setter applies, and where it does not, is in fields and forms.


A problem or a question? Write to support@oktul.com or open a request in the Help Center (opens in a new tab). Both open a request with a reference and an SLA.