API Reference#

Reference documentation for the globals and APIs available in Automan scripts.


Overview#

Automan scripts run in a secure sandboxed environment with access to specific globals for interacting with Jira. This page documents all available APIs.


Global Variables#

event#

The event object contains context data about what triggered the script execution. Its structure varies depending on the execution context.

Workflow Post Function Context#

{
  // Work item being transitioned
  issue: {
    id: string,        // Work item ID (e.g., "10001")
    key: string        // Work item key (e.g., "PROJ-123")
  },

  // Transition details
  transition: {
    id: string,
    name: string,
    from: { id: string, name: string },
    to: { id: string, name: string }
  },

  // Post function metadata
  postFunction: {
    id: string
  },

  // User who performed the transition
  atlassianId: string  // Atlassian account ID
}

Automation Action Context#

{
  // The evaluated smart value you configured
  smart: string,

  // Automation metadata
  automationId: string,

  "context": {
    "cloudId": string,
    "moduleKey": "automan-run-script",
    "userAccess": {
      "enabled": boolean,
      "hasAccess": boolean
    }
  },

  contextToken: string
}

Event Trigger Context#

{
  // Event type identifier
  eventType: string,  // e.g., "avi:jira:created:issue"

  // User who caused the event
  atlassianId: string,

  // Event-specific data (varies by event type)
  issue: { ... },     // For work item events
  comment: { ... },   // For comment events
  version: { ... },   // For version events
  changelog: { ... }  // For update events
}

Scheduled Script (Cron) Context#

{
  type: "cron",
  cronExpression: string,   // e.g., "0 9 * * *"
  scheduledTime: string     // ISO 8601 timestamp of scheduled execution
}

Test Execution Context#

{
  type: "test"
}

console#

Standard console object for logging.

console.log(…args)#

Logs messages to the execution log.

console.log("Simple message");
console.log("Multiple", "arguments", 123);
console.log("Object:", { key: "value" });

Note: Logs are captured and available in the Executions tab.


process.env#

Object containing environment variables configured in the Environment tab.

// Access a variable
const apiKey = process.env.API_KEY;

// Check existence
if (process.env.WEBHOOK_URL) {
  // Variable exists
}

// Default value pattern
const timeout = process.env.TIMEOUT || "30000";

See Environment Variables for more details.


Functions#

requestJira API#

The requestJira function makes authenticated requests to the Jira REST API.

Syntax#

const response = await requestJira(route, options, actAsUser);

Parameters#

Parameter Type Required Description
route Route Yes API route created with the route template tag
options Object No Request options (method, headers, body)
actAsUser String No Account ID to make the request as that user

Response#

Returns a Response object with:

  • ok - Boolean indicating success (status 200-299)
  • status - HTTP status code
  • statusText - HTTP status text
  • headers - Response headers
  • json() - Async method to parse JSON response body

Basic Usage#

// GET request (default)
const response = await requestJira(route`/rest/api/3/myself`);
const data = await response.json();

// POST request
const response = await requestJira(
  route`/rest/api/3/issue`,
  {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      fields: {
        project: { key: "PROJ" },
        summary: "New issue",
        issuetype: { name: "Task" }
      }
    })
  }
);

// PUT request
await requestJira(
  route`/rest/api/3/issue/${issueKey}`,
  {
    method: "PUT",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      fields: { summary: "Updated summary" }
    })
  }
);

// DELETE request
await requestJira(
  route`/rest/api/3/issue/${issueKey}`,
  { method: "DELETE" }
);

Acting as a User#

By default, requestJira makes requests as the app (system user). To make requests as a specific user, pass their Account ID as the third parameter:

// Make request as the current user (from event)
const userId = event.user?.accountId;
const response = await requestJira(
  route`/rest/api/3/issue`,
  {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      fields: {
        project: { key: "PROJ" },
        summary: "Issue created by user",
        issuetype: { name: "Task" }
      }
    })
  },
  userId  // Act as this user
);

When to use actAsUser:

  • Creating issues - The reporter will be the specified user
  • Adding comments - The comment author will be the specified user
  • Respecting permissions - The request will only succeed if the user has permission
  • Audit trail - Actions are logged as performed by the user

Important notes:

  • The user must have the necessary permissions for the action
  • If the user lacks permission, the request will fail with a 403 error
  • The Account ID can be found in event.user.accountId for event triggers

Example: Create Issue as User#

// Get the user who triggered the event
const triggerUserId = event.user?.accountId;

if (triggerUserId) {
  // Create a subtask as the triggering user
  const response = await requestJira(
    route`/rest/api/3/issue`,
    {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        fields: {
          project: { key: event.issue.fields.project.key },
          parent: { key: event.issue.key },
          summary: "Follow-up task",
          issuetype: { name: "Sub-task" }
        }
      })
    },
    triggerUserId
  );

  if (response.ok) {
    const newIssue = await response.json();
    console.log(`Created ${newIssue.key} as user ${triggerUserId}`);
  }
} else {
  console.log("No user context - creating as app");
  // Fallback to app context (no third parameter)
}

Error Handling#

Always check the response status:

const response = await requestJira(route`/rest/api/3/issue/${issueKey}`);

if (!response.ok) {
  const error = await response.json();
  console.log(`Error ${response.status}: ${response.statusText}`);
  console.log("Details:", JSON.stringify(error));
  return;
}

const issue = await response.json();

route#

Template tag function for building Jira API routes with proper encoding.

Usage#

// Basic usage
route`/rest/api/3/issue/${issueKey}`

// With multiple parameters
route`/rest/api/3/issue/${issueKey}/comment/${commentId}`

// Query parameters should be appended separately
const jql = "project = PROJ";
route`/rest/api/3/search/jql?jql=${encodeURIComponent(jql)}`

Why Use route?#

The route tag ensures:

  • Proper URL encoding of parameters
  • Correct route format for Jira API
  • Security against injection attacks

Always use route instead of string concatenation:

// Correct
const response = await requestJira(route`/rest/api/3/issue/${issueKey}`);

// WRONG - Don't do this
const response = await requestJira(`/rest/api/3/issue/${issueKey}`);

URLSearchParams#

Standard URLSearchParams class for building query strings.

const params = new URLSearchParams();
params.append("jql", "project = PROJ");
params.append("maxResults", "50");
params.append("fields", "summary,status");

const queryString = params.toString();
// "jql=project+%3D+PROJ&maxResults=50&fields=summary%2Cstatus"

// Or initialize with object
const params2 = new URLSearchParams({
  jql: "project = PROJ",
  maxResults: "50"
});

const queryParams = new URLSearchParams({
...(filterFields ? { fields: fieldsToDisplay } : {}),
});
route`/rest/api/3/issue/${issueKey}?${queryParams}`;

Common Jira API Patterns#

Get Current User#

const response = await requestJira(route`/rest/api/3/myself`);
const user = await response.json();
console.log(user.displayName, user.emailAddress);

Get Work Item Details#

const response = await requestJira(route`/rest/api/3/issue/${issueKey}`);
const issue = await response.json();

console.log("Key:", issue.key);
console.log("Summary:", issue.fields.summary);
console.log("Status:", issue.fields.status.name);
console.log("Assignee:", issue.fields.assignee?.displayName);

Update Work Item Fields#

await requestJira(
  route`/rest/api/3/issue/${event.issue.key}`,
  {
    method: "PUT",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      fields: {
        summary: "Updated Summary",
        description: {
          type: "doc",
          version: 1,
          content: [{
            type: "paragraph",
            content: [{ type: "text", text: "New description" }]
          }]
        },
        labels: ["label1", "label2"],
        customfield_10001: "Custom value"
      }
    })
  }
);

Add Comment#

await requestJira(
  route`/rest/api/3/issue/${issueKey}/comment`,
  {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      body: {
        type: "doc",
        version: 1,
        content: [{
          type: "paragraph",
          content: [{ type: "text", text: "Your comment here" }]
        }]
      }
    })
  }
);

Search Work Items (JQL)#

const jql = 'project = "PROJ" AND status = "In Progress"';
const response = await requestJira(
  route`/rest/api/3/search/jql?jql=${encodeURIComponent(jql)}&maxResults=50`
);
const result = await response.json();

console.log(`Found ${result.total} work items`);
for (const issue of result.issues) {
  console.log(issue.key, issue.fields.summary);
}

Assign Work Item#

await requestJira(
  route`/rest/api/3/issue/${issueKey}`,
  {
    method: "PUT",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      fields: {
        assignee: { accountId: "5a0000000000000000000001" }
      }
    })
  }
);

Create Work Item#

const response = await requestJira(
  route`/rest/api/3/issue`,
  {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      fields: {
        project: { key: "PROJ" },
        summary: "New Work Item Summary",
        issuetype: { name: "Task" },
        description: {
          type: "doc",
          version: 1,
          content: [{
            type: "paragraph",
            content: [{ type: "text", text: "Description text" }]
          }]
        }
      }
    })
  }
);
const newIssue = await response.json();
console.log("Created:", newIssue.key);

Transition Work Item#

// First, get available transitions
const transResponse = await requestJira(
  route`/rest/api/3/issue/${issueKey}/transitions`
);
const transitions = await transResponse.json();

// Find the transition you want
const targetTransition = transitions.transitions.find(t => t.name === "Done");

// Execute the transition
await requestJira(
  route`/rest/api/3/issue/${issueKey}/transitions`,
  {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      transition: { id: targetTransition.id }
    })
  }
);

Error Handling#

HTTP Errors#

const response = await requestJira(route`/rest/api/3/issue/${issueKey}`);

if (!response.ok) {
  console.log("HTTP Error:", response.status, response.statusText);
  const errorBody = await response.json();
  console.log("Error details:", errorBody);
  return;
}

const issue = await response.json();

Try/Catch#

try {
  const response = await requestJira(route`/rest/api/3/issue/${issueKey}`);
  const issue = await response.json();
  // Process work item...
} catch (error) {
  console.log("Error:", error.message);
}

Limitations#

Execution Time#

Scripts have a maximum execution time of 20 seconds. If a script exceeds this limit, it will be automatically terminated and return an error:

Script execution timed out after 20 seconds

Tips to avoid timeouts:

  • Minimize the number of API calls
  • Use JQL to filter data server-side rather than fetching all data and filtering in script
  • For bulk operations, consider using Scheduled Scripts with pagination
  • Avoid infinite loops and recursive operations without proper exit conditions

Example: Efficient pagination

// Process work items in batches to avoid timeout
const maxResults = 50;
let startAt = 0;
let hasMore = true;

while (hasMore && startAt < 200) { // Limit total to avoid timeout
  const response = await requestJira(
    route`/rest/api/3/search/jql?jql=${encodeURIComponent(jql)}&startAt=${startAt}&maxResults=${maxResults}`
  );
  const result = await response.json();

  for (const issue of result.issues) {
    // Process each work item
  }

  startAt += maxResults;
  hasMore = result.issues.length === maxResults;
}

API Rate Limits#

Be mindful of Jira API rate limits when making multiple requests.

No External HTTP#

Direct HTTP requests to external URLs are not supported. Use Jira API or store integration data in Jira.

No File System#

Scripts cannot access the file system.

No Imports#

External modules cannot be imported. Use the provided globals only.


Jira REST API Documentation#

For complete Jira REST API documentation, see: