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-connectfor the Generic Connectors@managed-api/jira-cloud-v3-sr-connectfor Jira Cloud API@managed-api/jira-service-management-cloud-sr-connectfor 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 (likeissueIdOrKey) - We instantiate API clients for both Kitchen and Supplier using their connection IDs
- Custom fields link the issues bidirectionally (
customfield_10128stores the Supplier key in Kitchen,customfield_10162stores 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:
- Define when the Automation should be triggered, e.g. Work item transitioned.
{ "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:
- Define when the Automation should be triggered, e.g: Work item commented.
{ "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:
- Define when the Automation should be triggered, e.g. Work item transitioned.
{ "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:
- Enabled Remote Workspace in ScriptRunner Connect settings
- Set up SFTP access to our ScriptRunner Connect instance
- Configured IntelliJ IDEA to connect via SFTP
- 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:
- Your IDE, Your Rules - Setting up SFTP and IDE configuration
- Testing Without Tears - Local testing strategies
- API Magic - Working with Jira APIs effectively
- 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
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.
Subscribe to our content
Do you want our content in your email? Among other things, we offer information on service management and customer experience development
More related
Atlassian
Brewing the Perfect Glögi: Exalate's Holiday Integration Recipe
See how Exalate synchronizes Jira Service Management instances with real-time comments, attachments, and status updates in a practical example.
Read more
Published 15.12.2025 | Updated 15.12.2025
Atlassian
Simplify Your Atlassian Integrations: How Generic Connector Reduces Costs and Complexity
Simplify Atlassian integrations with ScriptRunner's Generic Connector, reducing costs and complexity by consolidating multiple connectors into one per instance.
Read more
Published 3.11.2025 | Updated 03.11.2025
