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'slog. 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'svalue, and for a picked field `options(context)`, which returns the choices. `null` empties thefield.
| 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.
Related
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.