# Script API reference

> Every global, type and value an Oktul Code Studio script uses, as the TypeScript declarations the console's editor reads.
>
> Documentation for Oktul Code Studio, for Jira Cloud and Confluence Cloud, by Oktul: https://oktul.com/docs/plugins/code-studio/script-api/

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](../writing-scripts/).

## Globals and the context

```ts
/** 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.

```ts
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](../fields-and-forms/#dynamic-forms).

## Related

- [Writing scripts](../writing-scripts/)
- [Script examples](../script-examples/)
- [Limits](../limits/)

---

A problem or a question? Write to [support@oktul.com](mailto:support@oktul.com) or open a request in the [Help Center](https://oktul.atlassian.net/servicedesk/customer/portals). Both open a request with a reference and an SLA.
