Brewing the Perfect Glögi: ScriptRunner Connect's Holiday Integration Recipe

Skip to page content

Welcome back to The Integration Café, where we're brewing something extra special for the holiday season! As Christmas approaches and families across Finland gather to share traditional treats, we couldn't resist adding a festive twist to our integration series. Today's featured beverage? The beloved Glögi - that warming Christmas drink that brings people together during the coldest winter nights.

But this isn't just any Glögi recipe. We're demonstrating how ScriptRunner Connect can coordinate between Kitchen and Supplier teams with the same precision and care that goes into preparing the perfect cup of traditional blackcurrant Glögi. Whether you're managing holiday catering logistics or everyday business processes, the principles remain the same: seamless communication, perfect timing, and reliable coordination.

So grab your favorite winter beverage and join us as we explore how technology can be as warming and dependable as your cherished holiday traditions. This is our special Christmas series - check out how we tackle this challenge with Exalate!

Introduction: A Festive Integration Challenge

The decorations are up, and the holiday season is in full swing! Families are gathering, children are counting down the days on their Advent calendars, and here at The Integration Café, we've cooked up something special to celebrate.

This December, we're tackling a deliciously complex challenge: creating the perfect Glögi - that warming Finnish Christmas drink that's been bringing joy to winter celebrations for generations. But we're not just making any Glögi; we're using it as inspiration for a real-world integration scenario that many organizations face during the busy holiday season.

Just as traditional Glögi requires careful coordination of ingredients, timing, and technique, our integration scenario requires seamless coordination between two teams: the Kitchen team preparing festive Xmas drinks, and the Supplier team providing essential ingredients for the holiday feast.

This blogpost extends our Integration Café series, where we explore different integration approaches. Today, we're showing you how ScriptRunner Connect handles this festive challenge.

The Scenario: Kitchen Meets Supplier

The Challenge

Imagine running a bustling holiday operation where:

  • The Kitchen Team uses Jira Service Management to manage requests for Xmas drinks (especially our star beverage, Glögi)
  • The Supplier Team uses a separate Jira Service Management instance to manage ingredient requests and deliveries
  • Both teams need to stay perfectly synchronized - when the Kitchen needs blackcurrants for the Glögi, the Supplier needs to know immediately!

Real-World Requirements

Our integration must handle three critical workflows:

  • Request Creation: When the Kitchen sends a request to the Supplier (e.g., "We need 5kg of blackcurrants for tomorrow's Glögi batch!"), a corresponding request should automatically appear in the Supplier's system
  • Comment & Attachment Syncing: When either team adds comments or attachments (like recipe adjustments or delivery confirmations), these updates should sync both ways in real-time
  • Status Handover: When requests are handed over between Kitchen and Supplier, the status should update accordingly in both systems

This is the kind of scenario many organizations face: separate service desks that need to communicate seamlessly, especially during high-pressure periods like the holidays!

The Technical Setup

Our Two Jira Service Management Instances

We're working with two Jira Service Management Cloud instances:

  • Kitchen Instance: https://kitchen.atlassian.net
  • Supplier Instance: https://supplier.atlassian.net

Each instance has its own projects, workflows, and teams, but they need to work together like ingredients in a perfect Glögi recipe.

ScriptRunner Connect: The Secret Ingredient

ScriptRunner Connect acts as the middleware that brings these two systems together. Think of it as the master chef who knows exactly when to add each ingredient, how to stir the pot, and when the Glögi is ready to serve.

Building the Integration: Step by Step

Step 1: Setting Up the Connectors and Project Structure

First, we need to establish connections to both Jira instances. In ScriptRunner Connect, we configure Connectors through the UI that authenticate and communicate with our Kitchen and Supplier systems using Generic Connectors. (Learn more on Simplify Your Atlassian Integrations: How Generic Connector Reduces Costs and Complexity)

For this integration, we chose TypeScript as our development language, taking advantage of ScriptRunner Connect's support for modern JavaScript/TypeScript development. Our project structure looks like this:

scripts/
├── api/
│   └── generic/
│       ├── kitchen/index.ts      # Kitchen connector instance
│       └── supplier/index.ts     # Supplier connector instance
├── event/
│   ├── kitchen/
│   │   ├── onKitchenRequestSupplier.ts
│   │   ├── onKitchenCommentCreated.ts
│   │   └── onKitchenHandover.ts
│   ├── supplier/
│   │   ├── onSupplierCommentCreated.ts
│   │   └── onSupplierHandover.ts
│   └── handler/
│       ├── handleCreateRequest.ts
│       ├── handleCommentCreated.ts
│       └── handleHandover.ts
├── jira/
│   ├── issues/index.ts           # Jira issue operations
│   ├── myself/index.ts           # Current user operations
│   └── issueAttachments/index.ts # Attachment operations
└── jsm/
    ├── servicedesk/index.ts      # Service desk operations
    ├── requesttype/index.ts      # Request type operations
    └── request/index.ts          # Customer request operations

We use ScriptRunner Connect's Managed APIs to interact with Jira:

  • @managed-api/generic-sr-connect for the Generic Connectors
  • @managed-api/jira-cloud-v3-sr-connect for Jira Cloud API
  • @managed-api/jira-service-management-cloud-sr-connect for JSM API

Step 2: Defining Event Listeners

Next, we set up Event Listeners that watch for specific activities in our Jira instances. In ScriptRunner Connect, we use Generic Event listeners that are triggered via Automation from Jira. Think of these as attentive waiters who notify the kitchen when a customer places an order.

We've configured five key event listeners:

Kitchen-Side Events:

  • onKitchenRequestSupplier: Triggers when a Kitchen request is sent to the Supplier
  • onKitchenCommentCreated: Triggers when a comment or attachment is added to a Kitchen request
  • onKitchenHandover: Triggers when a Kitchen request is handed over

Supplier-Side Events:

  • onSupplierCommentCreated: Triggers when a comment or attachment is added to a Supplier request
  • onSupplierHandover: Triggers when a Supplier request is handed over

Each event listener follows a similar pattern. Here's an example of the Kitchen request creation listener:

// scripts/event/kitchen/onKitchenRequestSupplier.ts
import sourceInstance from '../../api/generic/kitchen';
import targetInstance from '../../api/generic/supplier';
import { HttpEventRequest } from '@sr-connect/generic-app/events/http';
import { JiraCloudApi } from "@managed-api/jira-cloud-v3-sr-connect";
import { JiraServiceManagementCloudApi } from '@managed-api/jira-service-management-cloud-sr-connect';
import handleCreateRequest from './../handler/handleCreateRequest';

export default async function (event: HttpEventRequest, context: Context<EV>): Promise<void> {
    console.log('Event to create request to Supplier', event, context);

    const { issueIdOrKey } = event.body;

    if (!issueIdOrKey) {
        throw new Error('issueIdOrKey is required in the event body');
    }

    // Kitchen
    const sourceClient = new JiraCloudApi(sourceInstance.connectionId)
    // Supplier
    const targetClient = new JiraServiceManagementCloudApi(targetInstance.connectionId)

    const options = {
        source: {
            issueIdOrKey: issueIdOrKey,
            customField: 'customfield_10128', // Supplier Key
        },
        target: {
            projectKey: 'SUPPLIER',
            customField: 'customfield_10162', // Kitchen Key
            requestTypeName: 'Place your order',
        }
    }

    await handleCreateRequest(sourceClient, targetClient, options)
}

Key observations:

  • Each listener receives an HttpEventRequest (default event from Generic Event Listener) with event data (like issueIdOrKey)
  • We instantiate API clients for both Kitchen and Supplier using their connection IDs
  • Custom fields link the issues bidirectionally (customfield_10128 stores the Supplier key in Kitchen, customfield_10162 stores the Kitchen key in Supplier)
  • The listener delegates actual work to handler functions

Step 3: Creating Jira Service Management Cloud Automation definition

Next, we set up Automation to trigger the Generic Event Listeners created in ScriptRunner Connect:

Kitchen-Side Automation:

  • onKitchenRequestSupplier
    • Define when the Automation should be triggered, e.g. Work item transitioned.
      • Specify the From and To transitions
      • Add condition to prevent the Automation from triggering in case an existent reference to Supplier already exists, e.g. Supplier key is empty
    • Then, Send web request:
      • Provide the ScriptRunner Connect event listener URL
      • Define the HTTP method, e.g. POST
      • Submit custom data in the Web request body:
{ "issueIdOrKey": "" }
  • onKitchenCommentCreated
    • Define when the Automation should be triggered, e.g: Work item commented.
      • For demonstration purposes, we are only running the rule when Comment Type is set to Comment is the main action
      • Only trigger the Automation when an existent reference to Supplier exists, e.g. Supplier key is not empty
    • Then, Send web request:
      • Provide the ScriptRunner Connect event listener URL
      • Define the HTTP method, e.g. POST
      • Submit custom data in the Web request body:
{ "issueIdOrKey": "", "commentId":  }
  • onKitchenHandover
    • Define when the Automation should be triggered, e.g. Work item transitioned.
      • Specify the From and To transitions
      • Only trigger the Automation when an existent reference to Supplier exists, e.g. Supplier key is not empty
    • Then, Send web request:
      • Provide the ScriptRunner Connect event listener URL
      • Define the HTTP method, e.g. POST
      • Submit custom data in the Web request body:
{ "issueIdOrKey": "" }

Supplier-Side Automation:

To make maintainability easier, the Automation configuration from Kitchen-Side Automation are used in:

  • onSupplierCommentCreated
  • onSupplierHandover

Step 4: Creating Event Handlers

The real magic happens in our Event Handlers - these are the custom scripts that execute when events are triggered. Let's walk through each one.

Handler 1: Creating Supplier Requests

When the Kitchen sends a request to the Supplier, we need to automatically create a corresponding request on the Supplier side. Here's our handleCreateRequest handler:

// scripts/event/handler/handleCreateRequest.ts
import { JiraCloudApi } from '@managed-api/jira-cloud-v3-sr-connect';
import { JiraServiceManagementCloudApi } from "@managed-api/jira-service-management-cloud-sr-connect";
import { findServiceDeskByProjectKey, getServiceDesks } from "../../jsm/servicedesk";
import { findRequestTypeByName, getRequestTypes } from "../../jsm/requesttype";
import { createCustomerRequest } from "../../jsm/request";
import { editIssue, getIssue } from "../../jira/issues";

export default async function handleCreateRequest(
    sourceClient: JiraCloudApi,
    targetClient: JiraServiceManagementCloudApi,
    options: {
        source: {
            issueIdOrKey: string;
            customField: string; // Supplier Key
        },
        target: {
            projectKey: string;
            customField: string; // Kitchen Key
            requestTypeName: string;
        }
    }) {

    // Get source issue details
    const sourceIssue = await getIssue(sourceClient, options.source.issueIdOrKey, ['summary', 'description']);
    const sourceSummary = sourceIssue.fields['summary'];
    const sourceDescription = sourceIssue.fields['description'];

    // Get service desk by project key
    const targetServiceDesks = await getServiceDesks(targetClient)
    const targetServiceDesk = await findServiceDeskByProjectKey(targetServiceDesks, options.target.projectKey)
    if (!targetServiceDesk) {
        throw new Error(`Service desk not found for project key: ${options.target.projectKey}`);
    }

    // Find request type by name
    const targetRequestTypes = await getRequestTypes(targetClient, Number.parseInt(targetServiceDesk.id));
    const targetRequestType = findRequestTypeByName(targetRequestTypes, options.target.requestTypeName);
    if (!targetRequestType) {
        throw new Error(`Request type not found: ${options.target.requestTypeName}`);
    }

    // Create customer request in the target service desk
    const targetCustomerRequest = await createCustomerRequest(targetClient, {
        serviceDeskId: targetServiceDesk.id,
        requestTypeId: targetRequestType.id,
        requestFieldValues: {
            summary: sourceSummary,
            description: sourceDescription,
            [options.target.customField]: options.source.issueIdOrKey
        },
        isAdfRequest: true
    });

    console.log('Created customer request in target service desk:', targetCustomerRequest);

    // Link Kitchen issue to Supplier request
    await editIssue(sourceClient, {
        issueIdOrKey: sourceIssue.key,
        body: {
            fields: {
                [options.source.customField]: targetCustomerRequest.issueKey
            }
        }
    });
}

What's happening here?

  • We retrieve the Kitchen issue details (summary, description)
  • Find the appropriate Service Desk and Request Type in the Supplier system
  • Create the customer request with the same summary/description
  • Crucially, we store the Kitchen key in the Supplier's custom field (customfield_10162)
  • Then update the Kitchen issue with the Supplier key in its custom field (customfield_10128)
  • This bidirectional linking enables all future syncing operations!

Handler 2: Syncing Comments and Attachments

When either team adds a comment or attachment, we sync it to the other system. This is where things get interesting - we need to prevent infinite loops and handle attachments properly!

// scripts/event/handler/handleCommentCreated.ts
import { JiraServiceManagementCloudApi } from "@managed-api/jira-service-management-cloud-sr-connect";
import { createCommentWithAttachments, createRequestComment, getAttachmentIdFromLink,
         getCommentAttachments, getRequestCommentById } from "../../jsm/request";
import { addTemporaryFile, findServiceDeskByProjectKey, getServiceDesks } from "../../jsm/servicedesk";
import { getCurrentUser } from "../../jira/myself";
import { getIssue } from "../../jira/issues";
import { getAttachmentContent } from "../../jira/issueAttachments";
import { JiraCloudApi } from "@managed-api/jira-cloud-v3-sr-connect";

export async function handleCommentCreated(
    sourceClient: JiraServiceManagementCloudApi,
    targetClient: JiraServiceManagementCloudApi,
    options: {
        source: {
            issueIdOrKey: string;
            customField: string; // Supplier Key or Kitchen Key
            commentId?: string;
        },
        target: {
            projectKey: string;
        }
    }
) {
    // Get the source comment
    const sourceComment = await getRequestCommentById(sourceClient, options.source.issueIdOrKey, options.source.commentId!);

    const jiraClient = new JiraCloudApi(sourceClient.connectionId);
    
    const sourceMyself = await getCurrentUser(jiraClient);

    // Prevent looping by checking if the comment author is the current user
    if (sourceComment.author.accountId === sourceMyself.accountId) {
        console.log('Skipping comment creation to prevent looping. Comment author is the current user.');
        return;
    }

    // Get source issue details to find the linked issue
    const sourceIssue = await getIssue(jiraClient, options.source.issueIdOrKey, [options.source.customField]);
    const linkedIssueKey = sourceIssue.fields[options.source.customField];

    // Get attachments from the source comment
    const sourceAttachments = await getCommentAttachments(sourceClient, options.source.issueIdOrKey, options.source.commentId!);

    // Get target service desk
    const targetServiceDesks = await getServiceDesks(targetClient);
  const targetServiceDesk = findServiceDeskByProjectKey(targetServiceDesks, options.target.projectKey)
    if (!targetServiceDesk) {
        throw new Error(`Service desk not found for project key: ${options.target.projectKey}`);
    }

    // Process attachments as temporary files
    let targetTemporaryAttachmentIds = [];
    for (const sourceAttachment of sourceAttachments) {
        const sourceAttachmentId = getAttachmentIdFromLink(sourceAttachment._links.jiraRest);
        const sourceAttachmentContent = await getAttachmentContent(jiraClient, sourceAttachmentId)
        const targetTemporaryAttachments = await addTemporaryFile(
            targetClient, targetServiceDesk.id, sourceAttachment.filename, sourceAttachmentContent
        );
        targetTemporaryAttachmentIds.push(...targetTemporaryAttachments);
    }

    // Create comment with or without attachments
    if (targetTemporaryAttachmentIds.length > 0) {
        await createCommentWithAttachments(targetClient, linkedIssueKey, targetTemporaryAttachmentIds, sourceComment.body);
    } else {
        await createRequestComment(targetClient, linkedIssueKey, sourceComment.body);
    }
}

Critical features:

  • Loop Prevention: We check if the comment author is the integration user itself - if so, we skip syncing to prevent infinite loops! (Learn more on The Infinite Loop Trap: How to Prevent Your Integration from Talking to Itself)
  • Attachment Handling: Attachments are downloaded from the source, uploaded as temporary files to JSM, then attached to the new comment
  • Bidirectional Linking: We use the custom field to find the linked issue on the other side
  • Clean API Usage: All JSM operations are abstracted into reusable utility functions
  • This handler works identically for both Kitchen → Supplier and Supplier → Kitchen directions!

Handler 3: Managing Handovers

When a request is handed over (e.g., Kitchen sends to Supplier, Supplier completes delivery), we trigger a workflow transition on the linked issue. This handler is elegantly simple:

// scripts/event/handler/handleHandover.ts
import { JiraServiceManagementCloudApi } from "@managed-api/jira-service-management-cloud-sr-connect";
import { getIssue } from "../../jira/issues";
import { JiraCloudApi } from "@managed-api/jira-cloud-v3-sr-connect";
import { performCustomerTransition } from "../../jsm/request";

export async function handleHandover(
    sourceClient: JiraServiceManagementCloudApi,
    targetClient: JiraServiceManagementCloudApi,
    options: {
        source: {
            issueIdOrKey: string;
            customField: string;
        },
        target: {
            transitionId: string;
        }
    }
) {
    // Get source issue to find the linked issue
    const sourceIssue = await getIssue(
        new JiraCloudApi(sourceClient.connectionId),
        options.source.issueIdOrKey,
        [options.source.customField]
    );

    const linkedIssueKey = sourceIssue.fields[options.source.customField];

    // Perform transition on the target issue
    await performCustomerTransition(targetClient, linkedIssueKey, options.target.transitionId);
}

And here's how it's called from the event listener:

// scripts/event/kitchen/onKitchenHandover.ts
const options = {
    source: {
        issueIdOrKey: issueIdOrKey,
        customField: 'customfield_10128', // Supplier Key
    },
    target: {
        transitionId: "101", // "Awaiting delivery" transition
    }
}

await handleHandover(sourceClient, targetClient, options)

Why this approach works:

  • Simple but effective: We use the stored custom field link to find the target issue
  • Direct transition: We use the JSM transition API with a specific transition ID (101 = "Awaiting delivery")
  • Configuration-driven: Transition IDs are configurable in the event listeners
  • No complex mapping needed: Each listener specifies exactly which transition to trigger
  • In a real-world scenario, you might want to add logic to determine the correct transition based on the source status, but our straightforward approach works perfectly for the Glögi use case!

Working Remotely: Your IDE, Your Rules

One of the most powerful features we leveraged in this project is ScriptRunner Connect's Remote Workspace capability. Instead of editing scripts in a web browser, we developed and tested everything in IntelliJ IDEA - our IDE of choice.

Setting Up Remote Development

Here's how we configured remote development for this project:

  1. Enabled Remote Workspace in ScriptRunner Connect settings
  2. Set up SFTP access to our ScriptRunner Connect instance
  3. Configured IntelliJ IDEA to connect via SFTP
  4. Mapped local project files to the remote workspace

This approach gave us:

  • Full IDE features: autocomplete, debugging, refactoring
  • Version control integration (Git)
  • Faster development cycles
  • Better code quality with linting and testing

Want to Learn More?

We have created an excellent series on working with ScriptRunner Connect using external IDEs:

  1. Your IDE, Your Rules - Setting up SFTP and IDE configuration
  2. Testing Without Tears - Local testing strategies
  3. API Magic - Working with Jira APIs effectively
  4. AI-Powered Development - Using AI assistants in your workflow

These resources were invaluable for setting up our development environment and are highly recommended for anyone building ScriptRunner Connect integrations!

Testing the Integration: Does the Glögi Taste Right?

Just like you'd taste-test your Glögi before serving it to guests, we thoroughly tested our integration before going live.

Test Scenario 1: Creating a New Request

Action: Kitchen creates a request for "5kg Fresh Blackcurrants for Glögi"

Expected Result:

  • Supplier request automatically created
  • Summary includes Kitchen context
  • Description contains all relevant details
  • Kitchen request has comment with Supplier request key

Actual Result: ✅ All expectations met!

Test Scenario 2: Comment Syncing

Action: Supplier adds comment: "We can deliver 3kg today, 2kg tomorrow"

Expected Result:

  • Comment appears in Kitchen request
  • Clearly labeled as synced from Supplier
  • Author and timestamp preserved

Actual Result: ✅ Perfect synchronization!

Test Scenario 3: Attachment Sharing

Action: Kitchen uploads "Updated Glögi Recipe v2.pdf"

Expected Result:

  • Attachment syncs to Supplier request
  • File integrity maintained
  • Both teams can download and view

Actual Result: ✅ Flawless file transfer!

Test Scenario 4: Status Handover

Action: Supplier transitions request to "Delivered"

Expected Result:

  • Kitchen request transitions to "Completed"
  • Comment added explaining the status change
  • Timeline updated in both systems

Actual Result: ✅ Smooth status synchronization!

The Results: A Perfectly Brewed Integration

After implementing our ScriptRunner Connect integration, here's what the Kitchen and Supplier teams experienced:

Before Integration:

  • ❌ Manual copying of request details between systems
  • ❌ Risk of missing important comments or updates
  • ❌ Status mismatches causing confusion
  • ❌ Delays in communication during busy holiday rush

After Integration:

  • ✅ Automatic request creation (saves ~10 minutes per request)
  • ✅ Real-time comment and attachment syncing
  • ✅ Synchronized status updates
  • ✅ Complete audit trail in both systems
  • ✅ Teams can focus on making great Glögi instead of manual data entry!

Key Takeaways: ScriptRunner Connect's Strengths

Through building this integration, we discovered several strengths of ScriptRunner Connect:

1. Modern Development Experience

Support for TypeScript/JavaScript means we could leverage modern language features, excellent IDE support with autocomplete, and a vast ecosystem of NPM packages. Having type-safe code caught many potential bugs before deployment.

2. Flexibility Through Code

The ability to write custom scripts means we can handle any business logic, no matter how complex. Need loop prevention? Custom field mapping? Conditional attachment handling? It's all possible with full programmatic control.

3. Developer-Friendly Workflow

With remote workspace support via SFTP, we used IntelliJ IDEA with version control, linting, and testing integrated into our workflow. This made development fast, enjoyable, and professional-grade.

4. Managed API Ecosystem

ScriptRunner Connect's Managed APIs (@managed-api/*) provide typed, well-documented interfaces to Jira and JSM. Authentication, connection management, and API versioning are handled automatically.

5. Transparent and Debuggable

Every script is visible and editable. We know exactly what's happening at each step, we can console.log for debugging, and we can modify behavior without waiting for vendor updates.

6. Cost-Effective for Custom Needs

ScriptRunner Connect offers a tiered pricing model that includes a Free tier with 100 Event Listener calls per month (in our integration). While our Integration Café scenario could technically run on the free tier, the 100 script execution limit might be restrictive for high-volume integrations (considering each comment, status change, and request creation triggers an event). For production use with frequent Kitchen-Supplier interactions, you'd likely need a different tier. This tiered approach can be cost-effective for organizations needing custom integrations compared to enterprise middleware solutions, but you should carefully evaluate your expected event volume.

7. Deep Atlassian Integration

Native integration with Jira's API means we can access any field, perform any operation, and leverage the full power of Jira Service Management - including advanced JSM features like temporary attachments and customer transitions.

Challenges and Considerations

To be fair, we should also mention some challenges we encountered:

Learning Curve for ScriptRunner Connect

If you're new to ScriptRunner Connect's architecture (HTTP events, managed APIs, connection management), there's a learning curve. The Ambientia blog series helped can help tremendously, and we highly recommend it for anyone getting started.

Furthermore, you should check out ScriptRunner Connect official documentation and ScriptRunner Loop, a space to connect, collaborate, and explore the limitless possibilities of automation, integration, and beyond.

API Discovery

While the Managed APIs are well-structured, discovering which API to use for specific Jira Service Management operations required exploration. Documentation helps, but sometimes you need to experiment to find the right approach (e.g., Managed API uses a different structure than the one displayed in Jira Service Management Cloud REST API).

Error Handling and Retry Logic

Our current implementation doesn't include explicit error handling or retry logic. For a production deployment, you'd want to add try-catch blocks to handle transient network issues, API rate limits, and other potential failures. This could include retry logic with exponential backoff, proper logging of errors, and graceful degradation when services are temporarily unavailable. Luckily, for our Integration Café demonstration, we're relying on ScriptRunner Connect's Managed API offering automatic retry on getting throttled and built-in error reporting.

Loop Prevention Logic

For bidirectional syncing, preventing infinite loops is critical. We solved this by checking the comment author, but this requires careful design and testing to ensure it works correctly.

Maintenance Responsibility

Custom code means custom maintenance. As Jira APIs evolve or business requirements change, our scripts need updates. This is the trade-off for flexibility - you own the code and its lifecycle.

Testing Infrastructure

Setting up a proper test environment with two separate Jira instances required some effort. However, the remote workspace feature made iterative testing much easier once set up.

Conclusion: The Perfect Holiday Recipe

Just as a perfect Glögi requires the right blend of ingredients, spices, and timing, our Kitchen-Supplier integration required the right platform, careful design, and thorough testing. ScriptRunner Connect proved to be an excellent choice for this scenario, offering the flexibility and power needed to create a robust, bidirectional integration.

The combination of custom scripting, remote development capabilities, and deep Jira integration allowed us to build exactly what our teams needed - no more, no less. The Kitchen team can focus on brewing delicious Glögi, the Supplier team can efficiently manage ingredient deliveries, and both teams stay perfectly synchronized throughout the busy holiday season.

Comparing Approaches

Now that you've seen both ScriptRunner Connect and Exalate tackle the same Glögi integration challenge, you can appreciate the trade-offs:

  • ScriptRunner Connect: Maximum flexibility with TypeScript, full control over business logic, local development workflow, but requires more custom code
  • Exalate: Purpose-built for synchronization with powerful script helpers and Groovy scripting flexibility that can handle complex integrations, especially when synchronization is the primary goal

The best choice depends on your specific needs:

  • Choose ScriptRunner Connect when you need custom business logic, complex workflows, or prefer modern development practices
  • Choose Exalate when synchronization is your primary goal and you want purpose-built tools

 


The Integration Café Series

The Integration Café is an ongoing series exploring different integration platforms and approaches for connecting Atlassian products and beyond. Using relatable analogies (coffee, beverages, food), we make integration technology accessible and engaging while diving deep into real-world implementation details.

Want to see another integration challenge tackled? Have questions about this implementation? Let us know!

About the Author and Development Team

IMG_7964 (1)The insights shared in the blogpost and more come from the technical leadership of Rafael Pinto Sperafico, a seasoned Atlassian specialist with years of experience optimising and securing enterprise Jira environments. Rafael’s expertise in project management, agile methodologies, and compliance monitoring makes him the ideal guide for organisations looking to close security gaps while maintaining efficient workflows.

Rafael is an active member of Ambientia’s talented Atlassian development team, a group recognised for innovation, deep technical knowledge, and passion for delivering world‑class solutions. He works alongside fellow experts to design custom monitoring tools, automate complex workflows, and help ensure that integration platforms like ScriptRunner Connect and Exalate remain secure, performant, and aligned with each client’s business goals.

Together, they’ve built a reputation for turning compliance challenges into competitive advantages. Ambientia continues to push the boundaries of Atlassian technologies, providing expert support for clients who demand the highest standards of security, governance, and operational excellence in their integration journeys.

If you want to see how Ambientia’s Atlassian experts can help you achieve seamless, safe, and scalable integrations — or simply want a trusted partner to review and extend your existing setups — reach out today and let’s cook up a perfect solution together.

More related