# What is Looply?

Looply is a cloud platform for the integration of your enterprise systems and Teams.

Looply brings SAP notifications and approvals into Microsoft Teams, so that users are informed instantaneously and decisions can be performed quickly and easily.\
\
This removes the need for users to go to SAP applications to deal with work; Looply brings the work to them. That means works gets done faster and easier with Looply, can be better managed and done in the place where the employee’s attention is already – in Teams.

Instead of relying on email, portal logins, or outdated inboxes, Looply lets users take action directly from Microsoft Teams — through rich, interactive Adaptive Cards that connect in real time to your enterprise systems.

{% embed url="<https://youtu.be/Xw8EiV108yk?si=dH33IiesMRjCBuYT>" %}

### Key Capabilities

* **Actionable Notifications**: Users can approve purchase orders, review time-off requests, or respond to SAP workflow events directly from Teams.
* **Bi-directional SAP Integration**: Looply connects to SAP ECC or S/4HANA via secure OData endpoints. It can read and post data back to SAP on behalf of the user.
* **Adaptive Card Framework**: Rich cards with buttons, fields, and layouts — all tied to dynamic business data.
* **No-code Workflow Builder**: Teams can define the logic that powers these cards using a visual designer — no scripting or infrastructure required.
* **Cloud-native Scaling**: Looply is built entirely on AWS Serverless. It scales automatically with your usage, whether you send 100 notifications or 1 million.
* **Flexible Identity Integration**: Looply supports Microsoft Entra ID (Azure AD) for authentication and delegated access.

***

## Looply Features

#### Template Designer

Looply’s embedded no-code/low-code adaptive card designer empowers developers to craft either simple or intricate notifications.

This AI powered designer has built-in ChatGPT support to assist developers to generate cards with just a prompt.

Notifications can be tailored and made responsive, ensuring that the right data reaches the right people in the most interactive manner possible.

<figure><img src="https://www.arch-global.com/wp-content/uploads/2023/10/template-editor.png" alt=""><figcaption><p>Adaptive card designer</p></figcaption></figure>

#### Workflow Studio

The Workflow Studio provides developers with the tools they need to streamline and automate business processes.

The drag-and-drop interface is designed to be intuitive, helping developers cut down on the development time that would typically accompany the design of complex integrations. They can visually define triggers, conditions, actions, and responses, resulting in a clear, easy-to-understand map of the process flow.

<figure><img src="https://www.arch-global.com/wp-content/uploads/2023/10/workflow-studio.png" alt=""><figcaption><p>Workflow Studio</p></figcaption></figure>

#### App Manager

The App Manager allows organisations to define and deploy unique bots to cater specifically for the needs of different parts of the enterprise.

It supports customised branding, app-specific user management and central app deployment through integration with Microsoft Azure.

<figure><img src="https://www.arch-global.com/wp-content/uploads/2023/10/app-manager.png" alt=""><figcaption><p>App Manager</p></figcaption></figure>

### Faster Decisions

#### Accelerate key business processes

With Looply, users see what needs to be actioned immediately, within Microsoft Teams, and can take action without leaving Teams.

This means that there is no need to wade through an email inbox to find items to action, or view multiple enterprise system Inbox tools.

Not only do users save valuable time, but the business processes are accelerated, delivering new process efficiencies.

### Process Transparency

#### Instant status updates for every process stakeholder

When Looply is used for internal notifications, process stakeholders see immediately when an event is triggered with the SAP system.

For approval workflows, cards are provided and updated to each user in the process, so that there is full visibility of when approval actions are taken.

This provides process transparency like never before, so that time is not wasted chasing others for actions or updates.

### More Effective Communication

#### Easy to find, dynamic and visual communication

Bring process notifications to users using graphical, adaptive cards.

These cards dynamically update as statuses and information changes, presenting up-to-date information in a easy, visually stimulating format.

As part of your digital transformation, you can redesign all your system-generated internal communications, and enjoy the benefits of having more engaged users.


# Deployment Models

Looply offers a range of flexible deployment models designed to meet the unique needs of enterprises across different industries, regulatory environments, and technical landscapes. All models are built on the same cloud-native, serverless core — allowing for infinite scalability, low operational overhead, and strong security by design.

This guide outlines each deployment model in detail, including technical ownership, use cases, and operational benefits.

***

### 1. Looply Public Cloud - Multi-Tenant SaaS&#x20;

Overview

This is the default and most common Looply deployment. Your organization is provisioned as a logically isolated tenant within our managed multi-tenant environment, hosted on AWS in the EU (Ireland) region.

Looply handles everything: provisioning, updates, scaling, backups, and security. All infrastructure is fully managed.

#### Ideal For

* Organizations that prefer a plug-and-play SaaS solution
* Enterprises with no internal DevOps or AWS teams
* Customers who need fast time-to-value

#### Benefits

* No infrastructure setup or maintenance
* Rapid onboarding — be live in hours
* Secure, enterprise-grade hosting with automatic scaling
* Patching and upgrades managed entirely by Looply
* Full role-based access control and audit logs included

#### Customer Responsibilities

* Sign up for Looply and invite team members
* Connect Microsoft Entra ID (Azure AD) tenant
* Install SAP ABAP add-on and expose required endpoints

***

### 2. Looply Private Cloud - Looply Managed

#### Overview

This model offers a balance between complete isolation and a managed service. Looply creates and manages a dedicated AWS sub-account on your behalf. The environment is exclusively yours but operated and monitored by Looply.

This gives you many of the benefits of private cloud with none of the infrastructure maintenance burden.

#### Ideal For

* Enterprises who want tenant isolation without taking on DevOps responsibilities
* Regulated organizations requiring dedicated resource control
* Customers scaling across business units or geographies

#### Benefits

* Dedicated AWS account, isolated from all other tenants
* Managed by Looply, including updates, observability, and maintenance
* Custom AWS region or security configuration available on request
* No infrastructure management required by customer

#### Customer Responsibilities

* Customers are required to complete all responsibilities listed under the Multi-Tenant SaaS model.
* Approve account provisioning and region selection
* Coordinate access for any enterprise-specific tools (optional)

***

### 3. Customer-Owned Private Cloud - Looply Managed

#### Overview

Looply can be deployed directly into your AWS account. In this model, Looply provides you with infrastructure templates and deployment tooling to provision and manage the environment within your own cloud footprint.

You maintain full visibility and control over the infrastructure while still leveraging Looply's cloud-native capabilities.

#### Ideal For

* Enterprises with strict internal policies requiring all systems to be hosted in their own AWS accounts
* Teams with DevOps maturity and AWS management capabilities
* Customers with custom networking, logging, or compliance tooling

#### Benefits

* Full infrastructure control (IAM, VPC, logging, observability)
* Deploy Looply alongside other internal workloads or data lakes
* Integrate with your enterprise monitoring and incident response stack
* Optional customization of VPC and subnet structure

#### Customer Responsibilities

* Customers are required to complete all responsibilities listed under the Multi-Tenant SaaS model.
* Maintain the underlying AWS environment (costs, IAM, upgrades)

#### Looply Responsibilities

* Deploy using Looply-provided CloudFormation or Terraform templates
* Provide infrastructure-as-code templates and versioning
* Push platform updates, security patches, and major features
* Provide integration support during provisioning and go-live

***

### Shared Benefits Across All Models

Regardless of the deployment model, all Looply instances come with:

* Auto-scaling, serverless architecture&#x20;
* Microsoft 365 and SAP integration capabilities
* Secure token-based authentication
* Real-time notifications, adaptive card support, and visual workflow builder
* Developer-friendly, no-code/low-code interface
* Monitoring, error handling, and audit logging by default

***

### Choosing the Right Deployment Model

Here’s a quick guide to help you select the best fit:

{% hint style="info" %}
Scroll horizontally to compare all deployment models listed the table below
{% endhint %}

<table><thead><tr><th width="336">Features</th><th width="491">Looply Public Cloud (Multi-Tenant, Looply-Managed)</th><th width="491">Looply Private Cloud (Dedicated Tenant, Looply-Managed)</th><th width="491">Looply Private Cloud (Customer-Managed)</th></tr></thead><tbody><tr><td>Who Manages Infrastructure?</td><td>Looply</td><td>Looply</td><td>Customer</td></tr><tr><td>Who Owns AWS Account?</td><td>Shared (Looply’s AWS environment)</td><td>Dedicated AWS account per customer (Managed by Looply)</td><td>Customer-provided AWS account</td></tr><tr><td>Best For</td><td>Businesses seeking cost-effective, fully managed SaaS with strong security</td><td>Enterprises needing compliance, data isolation &#x26; fully managed infrastructure</td><td>Highly regulated enterprises needing full control over infra</td></tr><tr><td>Data Isolation</td><td>Logical isolation via IAM, DynamoDB partitioning, and API security</td><td>Full physical isolation(dedicated AWS resources)</td><td>Full physical isolation (customer controls AWS setup)</td></tr><tr><td>Data Residency Control</td><td>Limited region support predefined by Looply (customer can choose from available regions)</td><td>Customer can choose any AWS region</td><td>Customer can choose any AWS region</td></tr><tr><td>Compliance Readiness</td><td>Secure by design built on SOC2/ ISO 27001 compliant AWS infrastructure</td><td>SOC 2 / ISO 27001 compliance-ready, with dedicated infrastructure</td><td>Customer responsible for compliance certification</td></tr><tr><td>Network Security</td><td>Multi-tenant architecture with IAM-based access controls, API Gateway protection, and AWS WAF. All traffic is encrypted in transit.</td><td>Dedicated VPC per customer, PrivateLink for internal AWS service access, tenant-specific IAM roles, and encrypted communications.</td><td>Dedicated VPC per customer, PrivateLink for internal AWS service access, customer-controlled network security policies, and encrypted communications.</td></tr><tr><td>Outbound API Access</td><td>Shared NAT Gateway with static IP for whitelisting</td><td>Dedicated NAT Gateway per customer with static IP, with optional PrivateLink-based outbound API access for enhanced security</td><td>Dedicated NAT Gateway per customer with static IP. Customers can choose to restrict outbound internet access entirely using VPC PrivateLink, ensuring private-only API connectivity</td></tr><tr><td>Security Model</td><td>IAM-based tenant isolation, API authentication, encryption (at rest &#x26; in transit), AWS GuardDuty monitoring</td><td>VPC isolation, PrivateLink, IAM-based role filtering, API authentication, encryption (at rest &#x26; in transit), AWS GuardDuty monitoring, tenant-specific IAM roles, dedicated security policies based on customer requirements</td><td>Identical to Private Cloud (Looply-Managed) </td></tr><tr><td>Provisioning &#x26; Access Control</td><td>Fully managed by Looply</td><td>Looply provisions and manages all infrastructure, including ongoing optimizations and updates</td><td>Looply provisions infrastructure in customer’s AWS account using securely shared credentials (e.g., temporary IAM roles, access vault). Infrastructure-as-Code (IAC) deployment requires client secrets (Serverless Framework). Customer is responsible for access governance after provisioning.</td></tr><tr><td>Software Updates</td><td>Automatic (Looply maintains version control &#x26; security patches)</td><td>Automatic (Looply ensures all instances remain at the latest service pack level)</td><td>Customer-defined(Looply provides updates, but customer led rollout)</td></tr><tr><td>Firefighter Access &#x26; Update Control</td><td>Full control (Looply manages everything, no customer intervention required)</td><td>Looply has full admin access for patches, updates, and troubleshooting</td><td>Looply requires ad-hoc 'Firefighter Access' for applying patches, upgrades, and troubleshooting. Access is temporary, controlled via IAM, and revoked after completion. </td></tr><tr><td>Support &#x26; Troubleshooting Model</td><td>Full support (Looply manages infrastructure, instant issue resolution with 24/7 monitoring )</td><td>Full support (Looply has direct access to logs, monitoring, and security patches. Response time SLAs are flexible and defined contractually based on business needs)</td><td>Customer IT team is primary support, Looply offers ‘Advisory Support’ with a troubleshooting SLA defined contractually, dependent on customer-provided access</td></tr><tr><td>Migration Flexibility</td><td>Can migrate to Private Cloud later(data export/import process)</td><td>Easier transition between Looply-Managed and Customer-Managed models</td><td>Customer fully responsible for migrating out</td></tr><tr><td>Customization &#x26; Integrations</td><td>Standard setup, limited customization</td><td>Some customization (branding, integrations, policies)</td><td>Full control over integrations &#x26; network security</td></tr><tr><td>Disaster Recovery &#x26; High Availability</td><td>Serverless architecture with automated scaling, multi-AZ support, and Looply-managed backups</td><td>Serverless architecture with automated scaling, multi-AZ support, and Looply provides automated backups by default, with customizable retention policies based on customer requirements</td><td>Serverless architecture with automated scaling, multi-AZ support, but customer responsible for backup policies</td></tr><tr><td>Performance &#x26; Scaling</td><td>Auto-scaling based on AWS Lambda &#x26; DynamoDB on-demand scaling</td><td>Looply-optimized scaling with dedicated tenant resources</td><td>Auto-scaling enabled, but customer governs configuration</td></tr><tr><td>Data Retention &#x26; Backup Policies</td><td>Fully Configurable retention &#x26; standard Looply defined backup policies</td><td>Fully Configurable retention &#x26; customizable backup policies</td><td>Fully Configurable retention &#x26; customizable backup policies</td></tr><tr><td>Infrastructure Cost</td><td>Included in Looply’s pricing (No additional AWS cost to customer)</td><td>Included in Looply’s pricing (No additional AWS cost to customer)</td><td>Customer pays directly for AWS infrastructure usage</td></tr><tr><td>Usage + Licensing Cost</td><td>Metered based on usage + flat subscription fee (includes hosting, management, and support)</td><td>Metered based on usage + Flat subscription fee (Looply manages software &#x26; updates)</td><td>Flat subscription fee (Looply provides software updates, but customer manages infrastructure)</td></tr></tbody></table>

### Not Sure Which Model Is Right For You?

Reach out to our team at <support@looply.ai> or schedule a deployment planning call. We’ll help you align hosting with your security, compliance, and operational goals.

> Architecture diagrams and data flow overviews available upon request.


# System Requirements

Minimal Requirements, Enterprise Ready

Looply is designed to integrate seamlessly into your existing Microsoft 365 and SAP environments with minimal disruption. Our architecture avoids invasive changes, enabling fast deployment while aligning fully with enterprise security and compliance standards.

This section outlines the required prerequisites across Microsoft 365, SAP, and network layers to help your IT team quickly assess readiness.

***

### Microsoft 365 Requirements *(For Microsoft Teams Integration)*

| Requirement                                     | Details                                                                                                                   |
| ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| **Microsoft Entra ID (Azure Active Directory)** | Required for user authentication, delegated access, and app management                                                    |
| **Microsoft Graph API Consent**                 | Admin consent required to grant Looply access to standard scopes for Teams app management and directory lookup            |
| **Microsoft Teams Client**                      | Desktop or Web client supported; Looply custom app must be installed via Teams Admin Center or organizational App Catalog |

***

{% hint style="info" %}
If your organization prefers not to connect its production Microsoft Azure AD or Microsoft Teams tenant during the Proof of Concept (POC) phase, Looply supports a fully isolated sandbox setup.

👉 Using a [Microsoft 365 Sandbox Tenant for Looply POC](/integrations/microsoft-integration/using-a-microsoft-365-sandbox-tenant-for-looply-poc)
{% endhint %}

### SAP System Requirements *(Only for SAP Workflow Integration)*

| Requirement                 | Details                                                                                                                       |
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| **SAP NetWeaver Version**   | 750 or higher (compatible with SAP ECC and SAP S/4HANA)                                                                       |
| **Looply ABAP Add-on**      | Installation required on SAP backend system                                                                                   |
| **OData Endpoint Exposure** | `/sap/opu/odata/looply/service_srv/*` and `/sap/bc/sec/oauth2/token` must be externally accessible to Looply's cloud services |
| **OAuth 2.0 Client Setup**  | Required in SAP Gateway to enable secure, token-based user delegation                                                         |
| **Network Accessibility**   | Outbound HTTPS access from SAP Gateway is required; static IP allowlisting supported if needed                                |

***

### Network and Security Requirements

| Requirement                    | Details                                                                                                                            |
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| **Outbound HTTPS Access**      | Required from SAP Gateway to Looply's public API endpoints                                                                         |
| **TLS Encryption**             | All communication encrypted using TLS 1.2+ standards                                                                               |
| **IP Allowlisting (Optional)** | Static outbound IP allowlisting available for customers enforcing strict egress policies                                           |
| **Authentication Standards**   | OAuth 2.0 delegated flows supported, including Basic Auth, OAuth Code Grant, and SAML2 Bearer Assertion (OBO) for SAP integrations |

***

If you have any questions about system compatibility or would like assistance with planning your integration, please reach out to <support@looply.ai>.


# SAP Integration: ABAP Add-on & Access

Looply connects seamlessly with SAP ECC and S/4HANA systems, enabling real-time notifications and workflow interactions inside Microsoft Teams.

This page explains the core components required to integrate Looply with your SAP landscape.

***

### Overview

To enable SAP integration, Looply requires:

* The installation of the **Looply ABAP Add-on** on your SAP Gateway system
* Configuration of OAuth 2.0 authentication in SAP Gateway
* Exposure of specific SAP endpoints externally (secure HTTPS access)

***

### Looply ABAP Add-on

The Looply ABAP Add-on is installed on your SAP system.

#### Purpose

* Exposes standardized OData services for workflow interactions
* Handles user delegation securely via OAuth 2.0 and SAML assertions
* Simplifies access management via predefined PFCG roles

#### Deployment

* Lightweight footprint; no core SAP modifications

{% hint style="info" %}
Installation instructions are detailed in the [SAP Integration Installation Guide](/integrations/sap-integration/installing-the-abap-looply-add-on).
{% endhint %}

***

### SAP OAuth 2.0 Client & Role Setup

To enable secure, delegated access between Looply and SAP:

* Create a new **OAuth 2.0 Client** in SAP Gateway
* Configure **SAML 2.0 Federation** between Azure AD and SAP
* Assign SAP users the necessary **PFCG roles** to authorize access to Looply services

Typical PFCG roles will grant controlled access to:

* OData services exposed by Looply
* Authorization token endpoints

**Note:** OAuth 2.0 setup must be completed separately for each SAP environment (DEV, QA, PROD).

***

### Making SAP Endpoints Accessible

Looply requires your SAP system to expose two key HTTPS endpoints externally:

| Endpoint                              | Purpose                          |
| ------------------------------------- | -------------------------------- |
| `/sap/opu/odata/looply/service_srv/*` | OData services for workflow      |
| `/sap/bc/sec/oauth2/token`            | OAuth 2.0 token exchange         |
| `/sap/bc/sec/oauth2/authorize`        | OAuth 2.0 authorization endpoint |

#### Options for Exposure

* **Proxy via Azure Application Gateway** (recommended)
* **Expose directly** through firewall/NAT rules&#x20;

{% hint style="success" %}
These endpoints must be accessible from Looply's hosted environment (AWS). AWS IP address for whitelisting Looply: **52.208.220.68**
{% endhint %}

If your SAP system is not externally exposed, we recommend setting up a secure proxy in Azure/applicable public facing infrastructure to enable it.

***

### Optional: SAP BTP Proxy Integration

If your organization uses SAP BTP Integration Suite and Cloud Connector, Looply can also connect to your SAP systems via BTP.

This model adds an extra layer of abstraction between Looply and your on-premise SAP Gateway.

* Useful for customers standardizing integration patterns via BTP

***


# Security & Identity - What IT Teams Need to Know

Looply is designed with enterprise-grade security, privacy, and identity management as foundational principles. This page explains how Looply handles authentication, authorization, data access, and secure integrations — specifically for IT and security professionals.

### Azure Tenant Connection

Looply uses Microsoft Graph APIs to perform the following actions with organizational consent:

* Deploy the Looply Teams app across your tenant
* Perform directory lookups to resolve users and identities
* Monitor and manage app installations

#### Required Graph API Permissions

The following permissions are requested during Azure tenant connection:

| Permission Name                                 | Purpose                                                       |
| ----------------------------------------------- | ------------------------------------------------------------- |
| `openid`, `offline_access`                      | Authentication and session token management                   |
| `User.Read`, `User.ReadBasic.All`               | Retrieve Teams user profiles                                  |
| `Team.ReadBasic.All`, `TeamMember.Read.All`     | Access Teams structure and membership                         |
| `AppCatalog.ReadWrite.All`, `AppCatalog.Submit` | Deploy Looply app to your Teams environment                   |
| `Presence.Read.All`                             | Used for future real-time card logic (optional)               |
| `TeamsAppInstallation.ReadWriteForUser`         | Manage Teams app installations for end users                  |
| `Directory.Read.All`                            | Look up directory data to map users between Microsoft and SAP |

These permissions must be approved by a Global Administrator or Privileged Role Administrator.

> 🔐 Looply does not store or access data outside these permissions. All access is authorized and logged.

### SAP Workflow Authentication Model

When a Microsoft Teams user interacts with an approval-bound notification generated by Looply (for example, approving a Purchase Order), Looply authenticates the user's action back into SAP on their behalf.

Looply currently supports the following authentication methods for SAP workflows:

| Authentication Type                      | Description                                                                                                                                                                                |
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Basic Authentication**                 | SAP Dialog user credentials are securely passed at runtime. Typically used in simpler or legacy SAP landscapes.                                                                            |
| **OAuth 2.0 Authorization Code Grant**   | Azure AD authenticated user flow exchanging OAuth tokens for SAP Gateway access.                                                                                                           |
| **SAML 2.0 Bearer Assertion (OBO Flow)** | Microsoft Teams user identity is propagated to SAP using a SAML Assertion issued by Azure AD, exchanged for an SAP OAuth token. Supports full delegated access without credential storage. |

> 📌 **Important:** This authentication approach is designed specifically for SAP ECC and S/4HANA workflows. Other systems may require different identity propagation models depending on their capabilities.

***

### Security Architecture Highlights

* **Data Access**: Looply does not store or cache SAP or Microsoft user data beyond workflow runtime. All data is processed in memory or temporarily held for workflow context.
* **Encryption**: All communication between Looply, Microsoft Graph, and SAP is encrypted over HTTPS using TLS 1.2+
* **Data Isolation**: Each customer tenant is logically and cryptographically isolated. Dedicated and private cloud models offer additional VPC-level isolation.
* **Role-Based Access Control (RBAC)**: Looply supports two roles: Admin and Developer. Admins manage Teams integration and users. Developers design workflows.
* **Audit Logging**: All admin actions and workflow runs are logged for traceability.

***

### Architecture Diagrams & Compliance Packages

Looply provides architecture reference diagrams, information security policy, security standards upon request. These are suitable for:

* Internal IT security reviews
* Governance or compliance assessments
* Risk analysis documentation

To request, please contact: <support@looply.ai>

***

### Next Step

Ready to set up your Looply account and invite your team? Head over to [Signing Up & Onboarding Your Team](https://academy.looply.ai/getting-started/signing-up-onboarding-team).


# Authenticating Teams User Actions to Enterprise Systems

## How Looply Connects Microsoft Teams Users to Enterprise Systems

Looply enables Microsoft Teams users to interact securely and seamlessly with enterprise workflows such as SAP approvals, without requiring separate logins, emails, or portal navigation.

This page explains how user identity is validated and securely propagated between Microsoft Teams, Looply, and enterprise backends.

***

### User Identity Mapping

When a notification is sent:

* Looply uses the user’s organizational email address to deliver the notification securely inside Microsoft Teams.
* All notification delivery is tied to the authenticated Microsoft Teams user session, ensuring the correct user receives and interacts with the workflow.

***

### Authenticating Users Back to Enterprise Systems

When a user acts on a notification (e.g., approve, reject, submit):

* Looply validates the user’s identity before committing any updates to the enterprise system.
* Authentication models vary based on the backend system’s capabilities.

***

### Supported Authentication Methods for SAP Integration

#### Basic Authentication

* SAP dialog user credentials are securely passed during the request.
* Suitable for simple SAP environments.

***

#### OAuth 2.0 Authorization Code Grant

* Users are securely redirected to SAP’s OAuth login page inside Microsoft Teams.
* SAP collects credentials and issues an authorization code, which Looply exchanges for an access token.
* The access token is then used to perform secure OData operations (e.g., POST, UPDATE) on SAP.

***

#### SAML 2.0 Bearer Assertion (OBO Flow)

* Looply leverages Microsoft Teams user session (authenticated with Azure AD).
* A SAML assertion is obtained from Azure AD and exchanged for an SAP OAuth token.
* As long as the Microsoft email matches the SAP SU01 email, Looply seamlessly impersonates the user in SAP.
* Enables true Single Sign-On (SSO) for SAP workflow approvals in Teams.

***

> 📌 The SAML2 OBO flow provides the most seamless and secure authentication experience, ensuring that users approve workflows without additional logins.

***

### Extending This Model Beyond SAP

Looply’s authentication framework is designed to be flexible.

It can be adapted to other enterprise systems that support OAuth 2.0 standards.

***

### Summary

* All user actions are validated securely before committing to enterprise systems.
* Only authorized users with matching identities can act on workflows.
* Authentication methods are configurable based on the customer’s backend system architecture.


# Signing Up & Onboarding Your Team

Getting started with Looply is a simple and secure process. This page walks you through setting up your Looply organization, inviting team members, and preparing your environment for integration.

***

### 1. Signing Up for a Looply Account

To begin, a designated team member (typically an IT admin, project lead, or business sponsor) should create the organization's Looply account.

* Visit: <https://dashboard.looply.ai/sign-up>
* Sign up using your enterprise email address (Microsoft Entra ID / Azure AD based email is preferred)

> 📌 **Important:**\
> The first user who signs up becomes the **Organization Owner (Super Admin)** by default. This user will manage key administration activities, including inviting other users, connecting Azure tenants, and managing integrations.

***

### 2. Inviting Team Members

After signing in:

1. Navigate to **Organisation → Team Manager** from the Looply admin cockpit sidebar
2. Click **Add Team Member**
3. Enter the email address of the colleague you want to invite
4. Assign a role to each user:

| Role          | Permissions                                                                                                                                      |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Admin**     | Full administrative access, including the ability to connect Azure tenant, deploy Teams app, manage users, and configure workflows               |
| **Developer** | Access to build and manage workflows, adaptive cards, and test integrations. No access to Azure tenant connection or organization-wide settings. |

5. Click **Send Invite** to complete the invitation process

> 📌 Admins can later modify user roles or deactivate accounts if responsibilities change.

***

### 3. Setting Up Azure Tenant Connection

To allow Looply to integrate with your Microsoft Teams environment, an Admin must connect your Microsoft Azure AD tenant.

Steps:

1. In the Looply admin cockpit, navigate to **Workflow → Integration**
2. Under **Microsoft Teams**, click **Connect**
3. Sign in using a **Global Administrator** or **Privileged Role Administrator** Azure AD account
4. Review and approve the Microsoft Graph API permission request
5. Select **Consent on behalf of your organization** and click **Accept**

This step enables:

* Deployment of the Looply app across your Microsoft Teams environment
* User directory lookup for workflow routing
* Management of app updates and permissions centrally

***

### 4. Preparing Your SAP Environment *(Only for SAP Integration Customers)*

If you plan to integrate Looply with SAP workflows:

* Install the Looply ABAP Add-on on your SAP Gateway system
* Configure an OAuth 2.0 client for secure delegated access
* Ensure relevant OData services are accessible externally or proxied securely
* Match SAP user emails (SU01) with Azure AD emails for identity propagation

👉 Full details are available in the [SAP Integration: ABAP Add-on & Access](https://academy.looply.ai/getting-started/sap-integration-abap-addon-access) section.

***

### 5. Best Practices During Onboarding

* Assign at least **two Admins** for redundancy
* Use **enterprise-managed accounts** (no personal emails)
* Coordinate early with your **SAP Basis team** if SAP integration is planned
* Validate outbound HTTPS access early to avoid network issues later

***

### Need Help?

If you encounter any issues or have questions during onboarding, please contact <support@looply.ai>.

Our solutions team can also schedule a 30-minute onboarding workshop to help your team accelerate deployment.

***

✅ Once onboarding is complete, you're ready to start building workflows, dispatching intelligent notifications, and modernizing your enterprise approvals!


# Looply Implementation Plan

This guide outlines the standard implementation phases for deploying Looply within your organization. Whether you are integrating only Microsoft Teams or extending to SAP workflows, this phased approach helps ensure a smooth, efficient rollout.

Looply provides project templates, onboarding workshops, and technical assistance throughout your journey.

### Download detailed implementation plan &#x20;

<table data-view="cards"><thead><tr><th></th><th data-type="files"></th></tr></thead><tbody><tr><td>General Integration plan (without SAP Plugin)</td><td><a href="/files/vo1Dg2LsWbPOH8Cr1fpX">/files/vo1Dg2LsWbPOH8Cr1fpX</a></td></tr><tr><td>SAP Integration plan (with SAP Plugin)</td><td><a href="/files/ROllxkr9KSMmTlXPuCAz">/files/ROllxkr9KSMmTlXPuCAz</a></td></tr></tbody></table>

### Phase 1: Mobilization

| Task                                                            | Primary Owner |
| --------------------------------------------------------------- | ------------- |
| Appoint Customer Project Lead                                   | Customer      |
| Mobilize SAP Basis Team (if SAP integration planned)            | Customer      |
| Arrange access to Microsoft Teams Admin Center and Azure Portal | Customer      |
| Confirm Looply Solution Architect engagement (if applicable)    | Looply        |

***

### Phase 2: Access Setup

| Task                                                                           | Primary Owner     |
| ------------------------------------------------------------------------------ | ----------------- |
| Sign up for Looply organization account                                        | Customer          |
| Invite Admins and Developers                                                   | Customer          |
| Connect Azure AD tenant to Looply                                              | Customer          |
| Deploy Looply custom app into Teams Admin Center or organizational App Catalog | Customer IT       |
| Schedule optional onboarding workshop with Looply Solutions Engineer           | Looply (optional) |

***

### Phase 3: SAP Integration Setup *(If Applicable)*

| Task                                                                  | Primary Owner        |
| --------------------------------------------------------------------- | -------------------- |
| Install Looply ABAP Add-on on SAP Gateway                             | Customer (SAP Basis) |
| Configure SAP OAuth 2.0 Client                                        | Customer (SAP Basis) |
| Expose SAP OData and OAuth endpoints externally (direct or via proxy) | Customer (SAP Basis) |
| Map Azure AD user emails with SAP SU01 email fields                   | Customer (SAP Basis) |

***

### Phase 4: Workflow Development & Testing

| Task                                                               | Primary Owner         |
| ------------------------------------------------------------------ | --------------------- |
| Build initial test workflows (pilot)                               | Looply/Customer       |
| Design Adaptive Cards for workflows                                | Looply/Customer       |
| Conduct internal UAT (User Acceptance Testing) with pilot group    | Customer Project Team |
| Looply Solutions Engineer provides best practice review (optional) | Looply (optional)     |

***

### Phase 5: Go-Live Preparation

| Task                                                   | Primary Owner               |
| ------------------------------------------------------ | --------------------------- |
| Finalize production deployment plan                    | Customer Project Manager    |
| Complete internal IT security and compliance approvals | Customer IT / Security Team |
| Validate error handling, monitoring, and workflow logs | Customer IT / Admins        |

***

### Phase 6: Production Launch and Hypercare

| Task                                                                | Primary Owner                               |
| ------------------------------------------------------------------- | ------------------------------------------- |
| Move workflows to Production status                                 | Customer Admin                              |
| Announce Looply availability to users (internal comms)              | Customer Communications Team                |
| Monitor production workflows for first 2-4 weeks (hypercare period) | Customer Admins / Looply Support (optional) |
| Hold retrospective and plan for further enhancements                | Customer Project Team                       |

***

### Notes

* SAP-related steps apply only if SAP workflow integration is part of your project scope.
* Looply Support (<support@looply.ai>) is available for optional onboarding sessions, integration assistance, and go-live hypercare support.

***

### Implementation Timeline&#x20;

> Timelines may vary based on the complexity of workflows, number of SAP systems, and customer internal readiness.

***

### Project Roles and Responsibilities

| Role                      | Responsibilities                                                                  |
| ------------------------- | --------------------------------------------------------------------------------- |
| Customer Project Manager  | Internal coordination, stakeholder updates                                        |
| IT Admin / Azure Admin    | Azure Tenant Connection, App Deployment                                           |
| SAP Basis Team            | ABAP Add-on installation, OAuth setup, Gateway exposure                           |
| Looply Solutions Engineer | Technical guidance, SAP/Teams integration support, workflow design best practices |
| End-User Champions        | Pilot testing, feedback during UAT                                                |

***


# Go-Live Readiness Checklist

This checklist helps ensure that all required steps are complete before moving Looply into production.\
It complements the broader Looply Implementation Plan by focusing specifically on final environment validation.

Use this checklist to review Microsoft Teams setup, Azure integration, SAP configuration (if applicable), network access, and internal readiness.

***

| Phase                           | Checklist Item                                                          | Owner                | Status (Ready/Not Ready) |
| ------------------------------- | ----------------------------------------------------------------------- | -------------------- | ------------------------ |
| Mobilization                    | Project team mobilized (Admins, Azure Admins, SAP Basis team)           | Customer             | ⬜                        |
| Mobilization                    | Teams Admin Center access confirmed                                     | Customer             | ⬜                        |
| Microsoft 365 Setup             | Looply organization account created                                     | Customer             | ⬜                        |
| Microsoft 365 Setup             | Azure AD tenant connected to Looply                                     | Customer             | ⬜                        |
| Microsoft 365 Setup             | Admin consent granted for Graph API permissions                         | Customer             | ⬜                        |
| Microsoft 365 Setup             | Looply custom app deployed into Teams Admin Center / App Catalog        | Customer             | ⬜                        |
| Microsoft 365 Setup             | Test notification sent and received by a test user                      | Customer             | ⬜                        |
| SAP Integration (If Applicable) | Looply ABAP Add-on installed and transported into SAP Gateway           | Customer (SAP Basis) | ⬜                        |
| SAP Integration (If Applicable) | OAuth 2.0 client configured and tested in SAP Gateway                   | Customer (SAP Basis) | ⬜                        |
| SAP Integration (If Applicable) | OData and OAuth endpoints securely exposed externally (direct or proxy) | Customer (SAP Basis) | ⬜                        |
| SAP Integration (If Applicable) | SAP user email mapping with Azure AD verified                           | Customer (SAP Basis) | ⬜                        |
| SAP Integration (If Applicable) | End-to-end workflow tested from Teams ➔ SAP ➔ confirmation              | Customer             | ⬜                        |
| Authentication Testing          | Basic Authentication flow tested (if applicable)                        | Customer             | ⬜                        |
| Authentication Testing          | OAuth 2.0 Code Grant flow tested (if applicable)                        | Customer             | ⬜                        |
| Authentication Testing          | SAML2.0 OBO flow tested (if applicable)                                 | Customer             | ⬜                        |
| Network & Security              | Outbound HTTPS access confirmed from SAP to Looply APIs                 | Customer IT          | ⬜                        |
| Network & Security              | TLS encryption validated (TLS 1.2+)                                     | Customer IT          | ⬜                        |
| Network & Security              | (Optional) Static IP allowlisting configured if needed                  | Customer IT          | ⬜                        |
| Testing & Validation            | Pilot group workflow testing completed successfully                     | Customer             | ⬜                        |
| Testing & Validation            | Error handling and audit logs verified inside Looply dashboard          | Customer             | ⬜                        |
| Final Sign-Offs                 | IT operational readiness approved                                       | Customer             | ⬜                        |
| Final Sign-Offs                 | Project stakeholders approved go-live                                   | Customer             | ⬜                        |
| Support Setup                   | Looply Support contacts documented (<support@looply.ai>)                | Customer             | ⬜                        |
| Support Setup                   | Post-Go-Live monitoring plan established (optional)                     | Customer             | ⬜                        |

***

✅ Once all checklist items are complete, you are ready to officially move Looply into production and empower users with modern, intelligent workflow approvals inside Microsoft Teams.

> 📌 Need additional assistance for Go-Live day support? Contact <support@looply.ai> to coordinate.


# Microsoft Integration

Connecting Looply with Microsoft Teams (Azure AD Integration)

Integrating Looply with your organization’s Microsoft 365 and Azure Active Directory (Azure AD) environment unlocks a secure, enterprise-grade connection between Looply and Microsoft Teams.

This connection enables your organization to seamlessly deploy Looply apps, deliver adaptive card notifications to employees, and manage authentication through Microsoft’s trusted identity platform — all with centralized control and auditability.

***

### Benefits of Connecting Looply to Microsoft

| Benefit                      | Description                                                                                                                                                                                      |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Seamless App Deployment      | Publish Looply-designed apps directly to your organization’s Teams App Catalog, making them instantly available to your workforce.                                                               |
| Centralized Management       | Admins can remotely install, update, and remove Looply apps across the organization while maintaining full visibility into usage and installations.                                              |
| Adaptive Card Notifications  | Send interactive Adaptive Cards — such as approval requests, alerts, or surveys — directly to Microsoft Teams users by their corporate email or Azure AD identity.                               |
| Single Sign-On (SSO)         | Users can sign into Looply apps using their existing Microsoft credentials, ensuring secure, frictionless authentication with Azure AD policies (e.g., MFA, Conditional Access).                 |
| Enterprise-Level Security    | Integration leverages Microsoft Graph APIs within consented permissions. All activity is logged and authorized by your organization’s Azure AD, ensuring compliance with corporate IT standards. |
| Improved Employee Engagement | Approvals, surveys, and notifications arrive directly where employees already collaborate — inside Teams — increasing response rates and reducing turnaround time.                               |

***

### How the Integration Works

Once your Microsoft tenant is connected, Looply communicates with Teams through secure Microsoft Graph API calls authorized by your organization’s Azure AD.

```
Looply Platform  ↔  Azure Active Directory  ↔  Microsoft Teams
      |                                          |
      |---- Authentication & Permissions --------|
      |---- App Deployment & Adaptive Cards -----|
```

All permissions are granted through organizational consent by an Azure AD administrator, and every interaction between Looply and Microsoft services is authenticated and auditable.

***

### Connecting Microsoft to Looply

[Only an Azure AD Global Administrator or Privileged Role Administrator can authorize this connection.](#user-content-fn-1)[^1]

#### Steps to Connect

1. Log in to the Looply Dashboard.
2. Go to Settings → Integrations → Microsoft.
3. Click “Connect” on the Microsoft integration card.
4. Sign in using your organization’s Microsoft admin account.
5. Review and approve the permission scopes displayed.
6. Once consent is granted, you’ll be redirected back to Looply, where your Microsoft tenant connection details will appear.

> The integration requires organizational consent to specific Microsoft Graph API permissions that enable app deployment, adaptive card delivery, and directory lookups. Looply does not request or store any data outside these approved scopes.

***

### Post-Connection Capabilities

After connecting, your organization can:

* Deploy and update Looply apps directly from the Teams admin center.
* Send adaptive cards to internal users and groups.
* Manage installations and monitor app usage within the Looply Integration Dashboard.
* Enable Microsoft-based authentication for Looply workflows and user actions.

***

### Further Reading

* [Security and Identity – What IT Teams Need to Know](/security-and-identity-what-it-teams-need-to-know)
* [Using a Microsoft 365 Sandbox Tenant for Looply POC](/integrations/microsoft-integration/using-a-microsoft-365-sandbox-tenant-for-looply-poc)
* [Authenticating Teams User Actions to Enterprise Systems](/authenticating-teams-user-actions-to-enterprise-systems)

***

[^1]:


# Using a Microsoft 365 Sandbox Tenant for Looply POC

During Proof of Concept (POC) phases, some organizations may prefer not to connect their enterprise Microsoft Azure Active Directory (Azure AD) or Microsoft Teams tenant to Looply’s public environment.

To accommodate this, Looply supports integration with a sandbox Microsoft 365 tenant—allowing end-to-end testing of approvals, notifications, and user interactions in Microsoft Teams without touching production systems.

This option helps IT and Cybersecurity teams safely validate the integration architecture while maintaining full isolation from live corporate data and users.

***

### Why Use a Sandbox Tenant

Connecting Looply to a sandbox Microsoft 365 tenant provides:

* A safe environment for testing Teams-based adaptive cards and workflow approvals
* No dependency on the enterprise tenant’s IT/Cyber approvals during POC
* Freedom to simulate user interactions and Teams notifications using fictitious accounts
* A clear migration path to the production tenant after internal review
* All data exchanged remains within Looply’s public instance and the sandbox Microsoft 365 environment.
* No production Azure AD or corporate identities are involved.

### Implementation Steps

| Step                                   | Description                                                                                                                                                                                                                                                                                                |
| -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1. Create a Sandbox Tenant             | Sign up for a Microsoft 365 E5 Developer Subscription through the [Microsoft 365 Developer Program](https://learn.microsoft.com/en-us/office/developer-program/microsoft-365-developer-program-get-started). This provides 25 user licenses, Azure AD directory, and a full Teams environment for testing. |
| 2. Configure Azure AD & Teams          | Register the Looply app within the sandbox Azure AD tenant and grant consent for Microsoft Graph API permissions (Teams activity, profile, email, etc.). Install the Looply Teams app within this sandbox environment.                                                                                     |
| 3. Simulate Users & Workflows          | Create fictitious Teams user accounts (e.g., <approver1@contoso.onmicrosoft.com>) to test adaptive card notifications, approvals, and responses end-to-end.                                                                                                                                                |
| 4. Maintain Isolation                  | The sandbox tenant remains fully isolated—no integration with production Azure AD or corporate identity systems is required. All activity and testing data remain confined to the sandbox.                                                                                                                 |
| 5. Transition to Production (Optional) | Once the POC is complete, the same configuration and app registration can be migrated to the enterprise tenant, following standard IT and Cyber governance approvals.                                                                                                                                      |

### Security & Compliance Alignment

* Looply authenticates against the sandbox tenant using the same OAuth 2.0 / SAML 2.0 flows as it would in production.
* The sandbox tenant provides full Azure AD identity control for realistic validation without production risk.
* All traffic remains encrypted (HTTPS / TLS 1.2+).

### References

* [Microsoft 365 E5 Developer Program – Get Started](https://learn.microsoft.com/en-us/office/developer-program/microsoft-365-developer-program-get-started)![Attachment.tiff](file:///Attachment.tiff)
* [Looply Security and Identity Documentation](https://academy.looply.ai/security-and-identity-what-it-teams-need-to-know)![Attachment.tiff](file:///Attachment.tiff)


# SAP Integration

This section describes how Looply can be integrated with SAP: How you can trigger a Looply workflow from your SAP system and how you can send data to SAP from a Looply Workflow


# Installing the ABAP Looply Add-On

In order to integrate Looply with SAP, you must first install the delivered ABAP Looply Add-On in your ECC or S/4HANA system.

## System Requirements

The following software components are required:

* SAP\_ABA (minimum supported release is 750)
* SAP\_BASIS (minimum supported release is 750)

## Performing the installation&#x20;

The Add-On can be installed using the standard SAP Add-On Installation Tool (SAINT). For detailed information on the SAINT tool, please see the [Installing and Upgrading Add-Ons ](https://help.sap.com/saphelp_nwmobile71/helpdata/en/18/e08d38dfc44765e10000009b38f842/content.htm)section of the SAP help portal. A quick overview of the required steps is also provided bellow:

* Logon to client 000 of your SAP system as a user that has SAP\_ALL authorization. Do not use the SAP\* or DDIC user.
* Go to transaction SAINT.
* Select Installation Package->Load Packages->From Front End and browse to the delivered .SAR file.
* Go to transaction SGEN.
* Select “Generate All Objects of Selected Software Components” and click Continue.
* Select the “Looply” software component and click Continue.
* Select for Parallel Generation: Leave default. Click Continue.
* Start Job Directly.
* Generation speed depends on your system but should take under 10 minutes.

Please note that all the above steps must first be performed in your development system and then repeated throughout your landscape.

## Post-Installation Tasks

After installing the Add-On, the following tasks must be performed:

### Create RFC Connection&#x20;

In transaction sm59, create an RFC connection to Looply. This will be used by the SAP system to trigger (or Resume) Looply workflows. Create a connection with the following details: (This step should be performed in every system in your landscape)

**Connection Type:** G\
**Target Host:** api.looply.io\
**Path Prefix:** /v2/workflows      &#x20;

<figure><img src="/files/R7Dlq8IMdCeZnRIRbkGZ" alt=""><figcaption></figcaption></figure>

In the *Logon & Security* tab, set SSL to active and select the correct SSL Certificate:&#x20;

<figure><img src="/files/blIyE92wI521QQZx3g4g" alt=""><figcaption></figcaption></figure>

At this point you may need to whitelist the Looply host so that i is not blocked by your firewall. When attempting a *Connection Test* you should get a 403 *Forbidden* response. You can ignore the 403 error

### Gateway Service Set-up

In this step we will configure the SAP Gateway service used by Looply to post data to SAP. The step depends on your setup. If you are running Gateway on the same system Looply is installed in, follow the steps in the [Gateway Service Setup - Single System](/integrations/sap-integration/installing-the-abap-looply-add-on/gateway-service-setup-single-system) page. If you are running Gateway on a Fiori Hub system, follow the steps in the [Gateway Service Setup - Hub scenario](/integrations/sap-integration/installing-the-abap-looply-add-on/gateway-service-setup-hub-scenario) page.

### Create API key

In the Looply web app, create an API key by going to the *API keys* tab and clicking on the *Create API Key* button.

<figure><img src="/files/OJpDFFAn02JPvd1z90lV" alt=""><figcaption></figcaption></figure>

### Configure System Settings

On your SAP system, go to the following transaction in SPRO and maintain an entry for each of the systems in your landscape:

General Application Functions -> Looply -> System Settings

* **SAP System ID:** The ID of your SAP system
* **RFC Destination:** The name of the sm59 connection created in the previous step&#x20;
* **API Key:** The API key created in the previous step (you can use the same API key for each system in your landscape)
* **Gateway System ID:** The ID of the corresponding Gateway System (if you are running Gateway on the same system Looply is installed in, you can leave this field blank)&#x20;
* **Gateway System Client:** The client of the corresponding Gateway System (if you are running Gateway on the same system Looply is installed in, you can leave this field blank)&#x20;
* **Profile:** Environment profile to be used when triggering a workflow (see [Environment Variables & Profiles](https://academy.looply.ai/workflows/environment-variables-and-profiles) )
* **Fiori Launchpad Host:** A common requirement is to include a url (or button) to a Fiori app on your Looply card. This field may be used to store the host used to construct the url.  &#x20;

These entries should then be transported throughout your landscape.

### Maintain Number Range Intervals

In transaction snro, maintain intervals for number range /LOOPLY/EI as follows: \
**Number Range No.**       01\
**From No.**                         0000000001\
**To Number**                       9999999999

This step must be repeated in every system of your landscape.

### &#x20;SAP Workflow Integration

If you wish to integrate Looply with SAP workflow, also follow the steps in [this section](/integrations/sap-integration/sap-workflow-integration#set-up).


# Gateway Service Setup - Single System

## Manage SAP System Alias

Manage SAP System Alias and make sure the SAP System Alias is assigned to LOCAL.

* In NW 7.0-731 go the IMG in transaction SPRO, and open SAP NetWeaver->Gateway->OData Channel->Configuration->Connection Settings->SAP Gateway to SAP System->Manage SAP System Aliases
* In NW 740+ go to the IMG in transaction SPRO and open SAP Netweaver->SAP Gateway->OData Channel->Configuration->Connection Settings->SAP Gateway to SAP System->Manage SAP System Aliases
* Alternatively you could also directly edit table /IWFND/V\_DFSYAL in sm30.

<figure><img src="/files/7PNMw1xIRWwrmeP3Nufy" alt=""><figcaption></figcaption></figure>

* **This step must be repeated in every system in your landscape.** \
  *See:* [*http://scn.sap.com/docs/DOC-42241*](http://scn.sap.com/docs/DOC-42241)&#x20;

## Activate SAP Gateway

Activate SAP Gateway in every system in the landscape (if it is not already activated).

* In NW 7.0-731 go the IMG in transaction SPRO ->SAP Netweaver->Gateway->OData Channel->Configuration->Activate or Deactivate SAP Gateway
* In NW 740+ go to the IMG in transaction SPRO->SAP Netweaver->SAP Gateway->OData Channel->Configuration->Activate or Deactivate SAP Gateway if you are on a NW 740 system) and make sure SAP Gateway is active.

## Assign Alias to oData Service

For the alias defined in the above step, create an entry in table /IWFND/V\_MGDEAM (via transaction sm30) for service /LOOPLY/SERVICE\_SRV\_0001.

<figure><img src="/files/X0Ni4qUVygOH6wZfWjc6" alt=""><figcaption></figcaption></figure>

## oData Service Authorization

In order for users to be able to post data to SAP (to approve a card for example), they must have authorization for the underlying oData service. To add this to a (new or existing) role, do the following:

* Add authorization object s\_service to the role
* Click on the “change” button next to “Program, transaction or functi”:

<figure><img src="/files/mov14IhJWQucF3K0hNDJ" alt=""><figcaption></figcaption></figure>

* Select “TADIR Service” in the “Type” drop-down and add the following two entries: \
  R3TR    IWSG   /LOOPLY/SERVICE\_SRV\_0001\
  R3TR    IWSV    /LOOPLY/SERVICE\_SRV                0001

<figure><img src="/files/oZ4lH6vCF7hmx8iyRtnc" alt=""><figcaption></figcaption></figure>

## Transport Changes

Transport all the changes made and new objects created in the above steps (including table entries) throughout your landscape.

## Activate sicf Node

In every system in your landscape, go to transaction SICF and activate the following node:

**/sap/opu/odata/looply/service\_srv**


# Gateway Service Setup - Hub scenario

The following steps must be performed on your gateway hub system. If you are you are running Gateway on the same system Looply is installed in, you may ignore this page.

## Create RFC Destination

If one does not already exist, create an SM59 destination which points from the gateway hub to the system where Looply is installed. It is recommended that a destination with the same name is created in all systems of the landscape (pointing to the relevant system each time) so that only 1 configuration entry covering all systems is created during the following steps.

## Create SAP System Alias

Create a System Alias using the RFC destination created in the previous step.

* In NW 740+ go to the IMG in transaction SPRO and open SAP Netweaver->SAP Gateway->OData Channel->Configuration->Connection Settings->SAP Gateway to SAP System->Manage SAP System Aliases
* In NW 7.0-731 go the IMG in transaction SPRO, and open SAP NetWeaver->Gateway->OData Channel->Configuration->Connection Settings->SAP Gateway to SAP System->Manage SAP System Aliases
* Alternatively you could also directly edit table /IWFND/V\_DFSYAL in sm30.

<figure><img src="/files/JneiLLpw0rVSMHR9uu2t" alt=""><figcaption></figcaption></figure>

## Maintain SAP Gateway Settings

Maintain SAP Gateway Settings using the RFC Destination and Alias created in the previous steps.

* In NW 7.0-731 go the IMG in transaction SPRO, and open SAP NetWeaver->Gateway Service Enablement->Backend OData Channel->Connection Settings to SAP Gateway->SAP Gateway Settings
* In NW 740+ go to the IMG in transaction SPRO and open SAP NetWeaver->SAP Gateway Service Enablement->Backend OData Channel->Connection Settings to SAP Gateway->SAP Gateway Settings
* Alternatively, you could also directly edit table /IWBEP/C\_SYSTEM in SM30.

<figure><img src="/files/e5qJ9KChNovvwTNoYlQo" alt=""><figcaption></figcaption></figure>

## Activate SAP Gateway

Activate SAP Gateway in every system in the landscape (if it is not already activated).

* In NW 7.0-731 go the IMG in transaction SPRO ->SAP Netweaver->Gateway->OData Channel->Configuration->Activate or Deactivate SAP Gateway
* In NW 740+ go to the IMG in transaction SPRO->SAP Netweaver->SAP Gateway->OData Channel->Configuration->Activate or Deactivate SAP Gateway if you are on a NW 740 system) and make sure SAP Gateway is active.

## Create Package

In transaction se21, create package ZLOOPLY\_SERVICE.

## Add Service

In transaction /N/IWFND/MAINT\_SERVICE click on “Add service”. Then insert your alias in the "System Alias" field, "/LOOPLY/\*" in the "Technical Service Name" field and click on the “Get Services” button:

<figure><img src="/files/xkLdync11pCLUWDLUo1s" alt=""><figcaption></figcaption></figure>

Select the table row and click on "Add Selected Services". Here you may change the technical names for service and model. Enter the package created in the previous step and click on the "Enable OAuth for Service" checkbox:

<figure><img src="/files/MJ8a3r65nD9aMs8FFWmg" alt=""><figcaption></figcaption></figure>

Back in transaction /N/IWFND/MAINT\_SERVICE, select the newly created service and click on "SAP Gateway Client" to test it:

<figure><img src="/files/kHD6X2HInwHLC1aDYpz7" alt=""><figcaption></figcaption></figure>

## oData Service Authorization <a href="#odata-service-authorization" id="odata-service-authorization"></a>

In order for users to be able to post data to SAP (to approve a card for example), they must have authorization for the underlying oData service. To add this to a (new or existing) role, do the following:

**In your Gateway hub system:**

* Add authorization object s\_service to the role
* Click on the “change” button next to “Program, transaction or functi”:

<figure><img src="/files/mov14IhJWQucF3K0hNDJ" alt=""><figcaption></figcaption></figure>

* Select “TADIR Service” in the “Type” drop-down and add the following entry: \
  R3TR IWSG ZSERVICE\_SRV\_0001

**In your ECC system:**

* Add authorization object s\_service to the role
* Click on the “change” button next to “Program, transaction or functi”:
* Select “TADIR Service” in the “Type” drop-down and add the following entry: \
  R3TR    IWSV    /LOOPLY/SERVICE\_SRV                0001

## Transport changes

Transport all the changes made and new objects created in the above steps (including table entries) throughout your landscape.

## Activate sicf node

**In the system where Looply is installed**,  go to transaction SICF and activate the following node:

**/sap/opu/odata/looply/service\_srv**


# Triggering or Resuming a Looply Workflow from SAP

## Triggering a Looply Workflow

You can trigger a Looply Workflow (your Workflow must start with an *Event Trigger* step - see the [Triggering Workflows page](/workflows/triggering-workflows) for more information) from SAP by calling the following method:&#x20;

**/LOOPLY/CORE=>TRIGGER\_WF**

The method has the following parameters:

<table><thead><tr><th width="248">Parameter Name</th><th width="101">Type<select><option value="7PeeNLqDkkwr" label="Import" color="blue"></option><option value="pPvjjLIx59Z2" label="Export" color="blue"></option></select></th><th width="135" data-type="checkbox">Mandatory</th><th>ABAP Type</th></tr></thead><tbody><tr><td>IM_LOOPLY_WF</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>true</td><td>CHAR36</td></tr><tr><td>IM_LOOPLY_WF_VERSION</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>false</td><td>INT1</td></tr><tr><td>IM_RECIPIENT</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>false</td><td>CHAR241</td></tr><tr><td>IM_SCENARIO</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>false</td><td>CHAR2</td></tr><tr><td>IM_SCENARIO_ID</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>false</td><td>CHAR32</td></tr><tr><td>IM_SCENARIO_VERSION</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>false</td><td>CHAR4</td></tr><tr><td>IM_STEP</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>false</td><td>NUMC3</td></tr><tr><td>IM_PROCESS_ID</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>false</td><td>CHAR48</td></tr><tr><td>IM_DATA</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>false</td><td>STRING</td></tr><tr><td>IM_UTIL_DATA</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>false</td><td>STRING</td></tr><tr><td>IM_TEST</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>false</td><td>FLAG</td></tr><tr><td>IM_PROFILE</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>false</td><td>CHAR30</td></tr><tr><td>EX_SUBRC</td><td><span data-option="pPvjjLIx59Z2">Export</span></td><td>false</td><td>SYSUBRC</td></tr><tr><td>EX_MESS</td><td><span data-option="pPvjjLIx59Z2">Export</span></td><td>false</td><td>BAPIRET2</td></tr><tr><td>EX_EXECUTION_ID</td><td><span data-option="pPvjjLIx59Z2">Export</span></td><td>false</td><td>CHAR36</td></tr></tbody></table>

**IM\_LOOPLY\_WF:** The ID of the Looply workflow you wish to trigger. To get the id you can click on the *Event Trigger* step in your Looply workflow and copy the *Request URL*. It will be of the following form:

[https://api.looply.io/v2/workflows/triggerworkflow/\<looply workflow id>/\<looply workflow version>?test=true](https://api.looply.io/v2/workflows/triggerworkflow/ff3d4d38-b25f-463a-a26e-baec25644c9a/1?test=true)

**IM\_LOOPLY\_WF\_VERSION:** The version of the Looply workflow you wish to trigger. If you leave this blank when making the call from SAP, the latest version of the workflow will be triggered.

**IM\_RECIPIENT:** You can use this (optional) parameter to easily pass a recipient for a card to Looply. You can also leave this blank and pass recipients inside the data or util\_data strings.&#x20;

**IM\_SCENARIO:** This (optional) parameter can be used for logging purposes or it can be passed back to SAP when making a POST request from Looply to determine which function module to run (see the [Triggering SAP code from Looply](/integrations/sap-integration/triggering-sap-code-from-looply) page for more information). The /LOOPLY/CORE class contains a number of constants for typical scenario values.

<figure><img src="/files/Ctczraw0tOEh6RM3Z3xd" alt=""><figcaption></figcaption></figure>

**IM\_SCENARIO\_ID**: Optional parameter which can be used for logging purposes or it can be passed back to SAP when making a POST request from Looply to determine which function module to run (see the [Triggering SAP code from Looply](/integrations/sap-integration/triggering-sap-code-from-looply) page for more information). Typical values include your Varo form type or SAP workflow id.

**IM\_SCENARIO\_VERSION:** Optional parameter which can be used for logging purposes or it can be passed back to SAP when making a POST request from Looply to determine which function module to run (see the [Triggering SAP code from Looply](/integrations/sap-integration/triggering-sap-code-from-looply) page for more information). Typical values include your Varo form version or SAP workflow version.

**IM\_STEP:** If you want to trigger multiple Looply workflows for the same scenario (the same Varo form type or SAP workflow for example) you can use this (optional) parameter to distinguish between them.

**IM\_PROCESS\_ID:** The process id of your Looply workflow. If you do not pass one in, Looply will assign one automatically. You need the process id if you wish to [resume a workflow](#resuming-a-looply-workflow). Looply does not allow multiple "In progress" instances with the same process id (although you can trigger a workflow instance with the process id of a previous workflow instance which has completed) so the parameter must have a unique value. Typical values include your Varo form id  or PO number. (Tip: include your system id in the process id to avoid clashes when workflows are triggered from different systems in your landscape).

**IM\_DATA:** Data you wish to pass to your Looply worklow. This will typically be in JSON format. (Tip: you can convert your ABAP internal table or structure to a JSON string by use of method  /ui2/cl\_json=>serialize).

**IM\_UTIL\_DATA:** Second data string you may pass to the workflow.

**IM\_TEST:** If you set this flag, a test request will be made to Looply. The data will become available in the *Workflow Studio* for binding, but the workflow will not actually be triggered.&#x20;

**IM\_PROFILE:** (available from version 110) Used to set an environment profile (see [Environment Variables & Profiles](https://academy.looply.ai/workflows/environment-variables-and-profiles) ) when triggering a workflow. If the import parameter is left blank, the code will check the "System Settings" configuration for a profile.

**EX\_SUBRC:** Return subrc code indicating whether the call to Looply worked or not.

**EX\_MESS:** Bapiret2 parameter containing success or error message.

**EX\_EXECUTION\_ID:** Id retrieved from Looply for logging purposes.

## Resuming a Looply Workflow&#x20;

Looply Workflows which are at an "Awaiting Response" status can be resumed from SAP by calling the following method:&#x20;

**/LOOPLY/CORE=>RESUME\_WF**

The method has the following parameters:

<table><thead><tr><th width="248">Parameter Name</th><th width="101">Type<select><option value="7PeeNLqDkkwr" label="Import" color="blue"></option><option value="pPvjjLIx59Z2" label="Export" color="blue"></option></select></th><th width="135" data-type="checkbox">Mandatory</th><th>ABAP Type</th></tr></thead><tbody><tr><td>IM_PROCESS_ID</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>true</td><td>CHAR48</td></tr><tr><td>IM_DATA</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>false</td><td>STRING</td></tr><tr><td>IM_UTIL_DATA</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>false</td><td>STRING</td></tr><tr><td>IM_MESS</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>false</td><td>STRING</td></tr><tr><td>IM_ACTION</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>false</td><td>STRING</td></tr><tr><td>IM_SCENARIO</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>false</td><td>CHAR2</td></tr><tr><td>IM_SCENARIO_ID</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>false</td><td>CHAR32</td></tr><tr><td>IM_SCENARIO_VERSION</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>false</td><td>CHAR4</td></tr><tr><td>IM_STEP</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>false</td><td>NUMC3</td></tr><tr><td>IM_RETRY</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>false</td><td>FLAG</td></tr><tr><td>EX_SUBRC</td><td><span data-option="pPvjjLIx59Z2">Export</span></td><td>false</td><td>SYSUBRC</td></tr><tr><td>EX_MESS</td><td><span data-option="pPvjjLIx59Z2">Export</span></td><td>false</td><td>BAPIRET2</td></tr><tr><td>EX_INTERNAL_CODE</td><td><span data-option="pPvjjLIx59Z2">Export</span></td><td>false</td><td>STRING</td></tr><tr><td>EX_HTTP_STATUS</td><td><span data-option="pPvjjLIx59Z2">Export</span></td><td>false</td><td>INT</td></tr></tbody></table>

**IM\_PROCESS\_ID:** The process id of the workflow you wish to resume.

**IM\_DATA:** Data you wish to pass to your Looply worklow. This will typically be in JSON format. (Tip: you can convert your ABAP internal table or structure to a JSON string by use of method  /ui2/cl\_json=>serialize).

**IM\_UTIL\_DATA:** Second data string you may pass to the workflow.

**IM\_MESS:** Often we may want to send a message to the end user when a workflow is resumed. This can easily be accomplished by using this parameter (of course messages can also be included in the IM\_DATA or IM\_UTIL\_DATA strings).&#x20;

**IM\_ACTION:** Workflows can be resumed in a number of ways for different reasons. We might for example want to resume a workflow when a request is approved, rejected, deleted etc. This parameter can provide an easy way to distinguish between the different "resume scenarios".

**IM\_SCENARIO:** Optional parameter used by the method for logging purposes. (see screenshot above for typical values)

**IM\_SCENARIO\_ID**: Optional parameter which can be used for logging purposes. Typical values  include your Varo form type or SAP workflow id.

**IM\_SCENARIO\_VERSION**: Optional parameter used by the method for logging purposes. Typical values include your Varo form version or SAP workflow version.

**IM\_STEP:** If you want to trigger multiple Looply workflows for the same scenario (the same Varo form type or SAP workflow) you can use this (optional) parameter to distinguish between them.

**IM\_RETRY:** (available from version 110) If this is flag is set and the resume fails because the workflow is not at a wait state, the resume will be attempted again (up to a maximum of 10 times) after a 1 second interval.

**EX\_SUBRC:** Return subrc code indicating whether the call to Looply worked or not.

**EX\_MESS:** Bapiret2 parameter containing success or error message.

**EX\_INTERNAL\_CODE:** (available from version 110) Internal code passed back from API call indicating a succes or reason for failure. See [the API documentation](https://academy.looply.ai/api-reference/workflow-api#resumeworkflow) for possible values.

**EX\_HTTP\_STATUS:** (available from version 110) Internal code passed back from API call indicating the status of the HTTP request. See [the API documentation](https://academy.looply.ai/api-reference/workflow-api#resumeworkflow) for possible values.

## Process Determination Configuration

Instead of passing hard-coded values into the above  parameters, it is good practise to maintain and read the *Looply Process Determination table  (/LOOPLY/ACTIVITY)*. This will also help the system to keep more accurate logs of your Looply activity. The table can be maintained via the following IMG transaction: \
SPRO->General Application Functions->Looply->Process Determination. \
Here, based on your *Scenario, Scenario Id, Scenario Version* and *Step* values you can determine which Looply workflow to trigger (or which SAP function module to call in an *Inbound* or *Workflow Outbound Scenario*)&#x20;


# Triggering SAP code from Looply

You can instruct your Looply workflow to make an HTTP request to SAP by using the *HTTP Request (SAP)* step. While you can make a request to any SAP endpoint, the ABAP Looply Add-On provides two endpoints with hook points to call your own custom code and these will be discussed here.

## POST Data to SAP

### Set-up on Looply

Configure your step as follows:

#### SAP Profile

Here you may Use the dropdown to select any of your pre-configured SAP Profiles that you have created and integrated with Looply.&#x20;

Often however you may want the workflow to send the request to the system in your landscape that it was triggered from, instead of maintaining a different workflow for your development, quality and production systems. You can do this by toggling the *Bind profile dynamically* switch and binding the *System ID* and *Client ID* fields with the respective fields in your payload. When Looply workflows are triggered by the /LOOPLY/CORE=>TRIGGER\_WF method, these fields are added to the payload automatically.

#### Service Path

Enter the following path: **/sap/opu/odata/looply/service\_srv/**

Note: If you have included a / at the end of the *Host URL* field in your SAP integration do not include it at the start of the service path as the values of the two fields are concatenated when the request is made.

#### Entity Name

Enter the following value: **DataInSe**t

#### Request Method

Select **POST**

#### Body

You may add the following fields to the request body:

<figure><img src="/files/Br2V5Lt2UCYHu7X2RSdR" alt=""><figcaption></figcaption></figure>

With the exception of field scenario\_id, the rest of the fields are optional and can be omitted. In the above screenshot, the first four fields are bound to fields in the payload (see section [Triggering a Looply Workflow](/integrations/sap-integration/triggering-or-resuming-a-looply-workflow-from-sap#triggering-a-looply-workflow) for more details) and will be used to determine the Z-function to be called once the request reaches the back-end. *data* contains additional data we want to send to the back-end and may include fields from the payload, information about a button the end-user clicked on, data entered into fields on the card (you can find more information on how to pass data from the card to the workflow [here](https://academy.looply.ai/integrations/sap-integration/pages/wgJsiyF3JzmHtJC4ZSj8#action.submit) ) etc. In this example we have used a function step which outputs a json string containing all these fields.

### Configuration in your SAP system

In your SAP system, create a new function module by copying sample function */LOOPLY/SAMPLE\_POST.* The function has the following signature:

<table><thead><tr><th width="175">Parameter Name</th><th width="78">Type<select><option value="7PeeNLqDkkwr" label="Import" color="blue"></option><option value="pPvjjLIx59Z2" label="Export" color="blue"></option></select></th><th width="116">ABAP Type</th><th>Description</th></tr></thead><tbody><tr><td>IM_DATA</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>STRING</td><td>Data passed in from Looply workflow. (corresponds to request body field <em>data</em> above)</td></tr><tr><td>EX_SUBRC</td><td><span data-option="pPvjjLIx59Z2">Export</span></td><td>SYSUBRC</td><td>Subrc code passed back to the workflow</td></tr><tr><td>EX_MESS</td><td><span data-option="pPvjjLIx59Z2">Export</span></td><td>BAPIRET2</td><td>Message to be logged in the SAP log and passed back to the workflow</td></tr><tr><td>EX_DATA</td><td><span data-option="pPvjjLIx59Z2">Export</span></td><td>STRING</td><td>Data passed back to Looply</td></tr></tbody></table>

TIP:  To convert an incoming json string to an internal table or structure you can use method /ui2/cl\_json=>serialize. To convert an internal table or structure to a json string to be passed back to Looply you can use method /ui2/cl\_json=>deserialize.

Next, make a configuration entry via the following IMG transaction: \
SPRO->General Application Functions->Looply->Process Determination\
Insert the values passed into the request body for fields *Scenario, Scenario ID, Version, Step* and the name of your newly created function module in field *Function.* You may leave fields *Workflow ID* and the non-key *Version* field blan&#x6B;*.* When the request reaches the back-end, the configuration table will be read based on the *Scenario, Scenario ID, Version and Step fields* and your function module will be called dynamically.&#x20;

## Download file from SAP

In certain scenarios, you may wish to include a button or a link to download a file/attachment from your sap system in a Microsoft Teams card. You can achieve this in the following way:

### Example - Adaptive Card

Insert a Container to your card. (You can use any container type that that has a "*Selection action*" property like a *ColumnSet* or *TableCells* if you have multiple attachments)

<figure><img src="/files/vFnPfrtDrtcce9M011WW" alt=""><figcaption></figcaption></figure>

If a gateway service endpoint which fetches the file already exists on your sap system you may use a url to that service. (Stelo users for example can include a url to Stelo service /STELO/APP\_SERVER\_SRV/AttContentSet to retrieve attachments in their payload. For example:\
[https://\<host>/sap/opu/odata/stelo/app\_server\_srv/AttContentSet('\<attachment cms document>')/$value](https://demo05.arch.co.uk:44300/sap/opu/odata/STELO/APP_SERVER_SRV/AttContentSet\('ACL-TID2-E-00-1000004307-0002-01'\)/$value) ) &#x20;

In other scenarios, an endpoint that fetches the file you want to download might not exist. In such cases you can use a url to the endpoint provided by the Looply ABAP add-on: [https://\<host>/sap/opu/odata/looply/service\_srv/AttSet(scenario='\<scenario>',scenario\_id='\<scenario\_id>',version='\<version>',step='\<step>',data='\<data used to get file>')/$value](https://fiori-dev.sunderland.gov.uk/sap/opu/odata/LOOPLY/SERVICE_SRV/AttSet\(scenario='WI',scenario_id='WS93000012',version='2',step='002',data='10400921__0045000241'\)/$value)

Using a url with the above format will allow you to call your own Z-function to return a file.  You can create such a Z-function by copying  sample function */LOOPLY/SAMPLE\_GET\_FILE.* The function has the following signature:

<table><thead><tr><th width="175">Parameter Name</th><th width="78">Type<select><option value="7PeeNLqDkkwr" label="Import" color="blue"></option><option value="pPvjjLIx59Z2" label="Export" color="blue"></option></select></th><th width="116">ABAP Type</th><th>Description</th></tr></thead><tbody><tr><td>IM_DATA</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>STRING</td><td>String from 'data' url parameter</td></tr><tr><td>EX_SUBRC</td><td><span data-option="pPvjjLIx59Z2">Export</span></td><td>SYSUBRC</td><td>Subrc code passed back to the workflow</td></tr><tr><td>EX_MESS</td><td><span data-option="pPvjjLIx59Z2">Export</span></td><td>BAPIRET2</td><td>Message to be logged in the SAP log and passed back to the workflow</td></tr><tr><td>EX_XDATA</td><td><span data-option="pPvjjLIx59Z2">Export</span></td><td>XSTRING</td><td>File data in XSTRING format</td></tr><tr><td>EX_MIME_TYPE</td><td><span data-option="pPvjjLIx59Z2">Export</span></td><td>STRING</td><td>File Mimetype</td></tr><tr><td>EX_FILENAME</td><td><span data-option="pPvjjLIx59Z2">Export</span></td><td>STRING</td><td>Filename</td></tr></tbody></table>

Next, make a configuration entry via the following IMG transaction: \
SPRO->General Application Functions->Looply->Process Determination\
Insert the values that match the url parameters for fields *Scenario, Scenario ID, Version, Step* and the name of your newly created function module in field *Function.* You may leave fields *Workflow ID* and the non-key *Version* field blan&#x6B;*.* When the request reaches the back-end, the configuration table will be read based on the *Scenario, Scenario ID, Version and Step fields* and your function module will be called dynamically.&#x20;


# SAP Workflow Integration

## Set-up

In order to integrate Looply with SAP workflow, you must first create a standard task: In transaction PFTC select *"Standard task*" as the *Task type* and click on the *Create* button:&#x20;

<figure><img src="/files/gGeGxbLZ7LR8dwrBMb4C" alt=""><figcaption></figcaption></figure>

Enter an *Abbreviation*, *Name* and *Work item text*. Select "*ABAP Class*" from the *Object Category* dropdown and enter "*/LOOPLY/WF\_OUT*" as the *Object Type* and *"TRIGGER"* or *"RESUME"* depending on whether you wish your standard task to trigger or to resume a Looply workflow. (You may of course create both a *Trigger* and a *Resume* task). Select the *Background processing* checkbox and click  *Save*. Click on *Yes* in the pop-up to transfer missing elements from the object method. Next, select the *Background processing* checkbox and *Save.* Make a note of the number assigned to your standard task:

<figure><img src="/files/4tBi9OKxLibLgaHD70GK" alt=""><figcaption></figcaption></figure>

The task only needs to be created once. It can then be used any number of times in different (or in the same) workflow(s).

## Triggering a Looply workflow from a SAP workflow

In transaction swdd, add a new activity to your workflow, and enter the number of your newly created task to the *Task* field, preceded by TS  (for example TS90000004) and press *Enter.* Dismiss the "Define Container Elements and Binding" pop-up as this will create unwanted fields in your container and instead perform the binding manually. You may bind data from your workflow (or pass in static-hardcoded values) to the following fields:

<table><thead><tr><th width="211">Parameter Name</th><th width="81">Type<select><option value="7PeeNLqDkkwr" label="Import" color="blue"></option><option value="pPvjjLIx59Z2" label="Export" color="blue"></option><option value="XVv1Ysr6YD4W" label="Changing" color="blue"></option></select></th><th width="182">ABAP Type</th><th>Description</th></tr></thead><tbody><tr><td>IM_WFD_ID</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>SWD_WFD_ID</td><td>Use to pass in the workflow id. This will then be used to read the "<em>Process Determination</em>" <em>configuration</em> for a matching Scenario Id to determine which Looply workflow to trigger.</td></tr><tr><td>IM_WF_VERSION</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>SWD_VERSIO</td><td>You may have multiple versions of the same SAP workflow and wish to trigger a different Looply workflow depending on the SAP workflow version. Use this field in order to achieve this. Please note that using the field is optional but it's value must match the value in the "<em>Process Determination</em>" <em>configuration</em></td></tr><tr><td>IM_STEP</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>/LOOPLY/STEP</td><td>You may wish to trigger different Looply workflows at different points of the same SAP workflow. Use this field in order to achieve this. Please note that using the field is optional but it's value must match the value in the "<em>Process Determination</em>" <em>configuration</em></td></tr><tr><td>IM_WORKITEMID</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>SWW_WIID</td><td>Can be used to pass in the workitem id. This can then be used to get data from the workflow container using function SAP_WAPI_READ_CONTAINER</td></tr><tr><td>IM_REC_UNAME</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>SWF_STRING</td><td>Not actually passed to Looply. Used in here in case you want to get data for a certain SAP user</td></tr><tr><td>IM_REC_EMAIL</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>SWF_STRING</td><td>Can be used to send a card to a certain user (of course you can bind the recipient to any field in the data you like on the Looply side)</td></tr><tr><td>IM_DATA</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>SWF_STRING</td><td>Can be used to pass in any (string) data from the workflow container</td></tr><tr><td>IM_PROCESS_ID_T</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>/LOOPLY/PROCESS_ID_T</td><td>Table of process ids of the Looply workflows that will be triggered (see below for more details)</td></tr><tr><td>IM_IGNORE_ERRORS</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>FLAG</td><td>If this flag is not set, any potential errors that occur while triggering Looply will raise an exception and stop the workflow execution. Setting this flag will ignore errors so that the workflow execution continues to the next step</td></tr><tr><td>EX_DATA</td><td><span data-option="pPvjjLIx59Z2">Export</span></td><td>SWF_STRING</td><td>Use to pass back any (string) data to the Workflow container</td></tr><tr><td>EX_PROCESS_ID_T</td><td><span data-option="XVv1Ysr6YD4W">Changing</span></td><td>/LOOPLY/PROCESS_ID_T</td><td>Table of process ids of the Looply workflows that have been triggered (see below for more details). You may want to pass this to the workflow container so that you know which Looply workflows to resume at a later stage</td></tr></tbody></table>

As part of the standard task to trigger Looply, core method /LOOPLY/WF\_OUT=>TRIGGER will be triggered. The method will read the ["*Process Determination*" *configuration*](/integrations/sap-integration/triggering-or-resuming-a-looply-workflow-from-sap#process-determination-configuration) based on the values of IM\_WFD\_ID, IM\_WF\_VERSION and IM\_STEP passed in above to determine which Looply workflow to trigger and a Z-function which will be called to get the data to be passed  in to the workflow. You can create such a function by copying sample function /LOOPLY/SAMPLE\_WF\_TRIGGER. Based on the binding done in the workflow step above, the following parameters will be available in the function:

<table><thead><tr><th width="193">Parameter Name</th><th width="91">Type<select><option value="7PeeNLqDkkwr" label="Import" color="blue"></option><option value="pPvjjLIx59Z2" label="Export" color="blue"></option><option value="XVv1Ysr6YD4W" label="Changing" color="blue"></option></select></th><th width="187">ABAP Type</th><th>Description</th></tr></thead><tbody><tr><td>IM_WORKITEMID</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>SWW_WIID</td><td>Passed in from binding parameter</td></tr><tr><td>IM_DATA</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>SWF_STRING</td><td>Passed in from binding parameter</td></tr><tr><td>EX_CONT_DATA</td><td><span data-option="pPvjjLIx59Z2">Export</span></td><td>SWF_STRING</td><td>Passed back to binding parameter EX_DATA</td></tr><tr><td>EX_SUBRC</td><td><span data-option="pPvjjLIx59Z2">Export</span></td><td>SYSUBRC</td><td>Assuming parameter IM_IGNORE_ERRRORS of the standard task has not been set to X via the binding, setting a subrc != 0, will cause an exception to be raised</td></tr><tr><td>EX_MESS</td><td><span data-option="pPvjjLIx59Z2">Export</span></td><td>BAPIRET2</td><td>When the exception is raised you can use this parameter to insert a certain message in the workflow log</td></tr><tr><td>CH_LOOPLY_DATA</td><td><span data-option="XVv1Ysr6YD4W">Changing</span></td><td>/LOOPLY/TRIGGER_DATA_T</td><td>(see below)</td></tr></tbody></table>

Table CH\_LOOPLY\_DATA can be pre-filled by the SAP workflow via the binding with one row for every row in the process id table IM\_PROCESS\_ID\_T. If IM\_PROCESS\_ID\_T is not bound to anything or left blank,   CH\_LOOPLY\_DATA will still be pre-filled with a row (with a blank process id) assuming binding parameters  IM\_RECIPIENT\_EMAIL or RECIPIENT\_UNAME are not blank. The table can be edited inside the Z-function, after which, one Looply workflow will be triggered for each row of the table with the relevant data. The table contains the following fields:

<table><thead><tr><th width="200">Field Name</th><th width="209">ABAP Type</th><th>Description</th></tr></thead><tbody><tr><td>PROCESS_ID</td><td>/LOOPLY/PROCESS_ID</td><td>The process id of the Looply workflow that will get triggered. Note that you can only have one active workflow per process id</td></tr><tr><td>RECIPIENT_EMAIL</td><td>AD_SMTPADR</td><td>Can be used to send a card to a certain user (of course you can bind the recipient to any field in the data you like on the Looply side)</td></tr><tr><td>RECIPIENT_UNAME</td><td>STRING</td><td>Not actually passed to Looply. Used in here in case you want to get data for a certain SAP user in your function</td></tr><tr><td>DATA</td><td>STRING</td><td>Data string we pass to Looply. You can use method /ui2/cl_json=>serialize to convert an internal table to a json string if needed</td></tr><tr><td>UTIL_DATA</td><td>STRING</td><td>Second data string in case you'd like to pass a second/separate string to Looply</td></tr></tbody></table>

Please note that filling in any of the above fields is optional. You can if you wish trigger a Looply workflow without passing in any data and without a process id (Looply will automatically assign a process id in this case)

<figure><img src="/files/6stTtYLl1PVaBy2gmvrx" alt=""><figcaption><p>Trigger Looply from SAP Workflow</p></figcaption></figure>

## Resuming a Looply workflow from a SAP workflow

You may also wish to resume a Looply workflow from your SAP workflow - you may have sent out a request for approval that should now be escalated, edited, or deleted for example. The mechanism for doing so is very similar to the mechanism for triggering a workflow. You must first add an activity to your workflow which points to a task that calls method /LOOPLY/WF\_OUT=> RESUME (instead of /LOOPLY/WF\_OUT=> TRIGGER). The fields available for binding will be slightly different in this case:

<table><thead><tr><th width="212">Parameter Name</th><th width="87">Type<select><option value="7PeeNLqDkkwr" label="Import" color="blue"></option><option value="pPvjjLIx59Z2" label="Export" color="blue"></option><option value="XVv1Ysr6YD4W" label="Changing" color="blue"></option></select></th><th width="182">ABAP Type</th><th>Description</th></tr></thead><tbody><tr><td>IM_WFD_ID</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>SWD_WFD_ID</td><td>Use to pass in the workflow id. This will then be used to read the "<em>Process Determination</em>" <em>configuration</em> for a matching Scenario Id to determine which Looply workflow to trigger.</td></tr><tr><td>IM_WF_VERSION</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>SWD_VERSIO</td><td>Use to determine the SAP workflow version. Please note that using the field is optional but it's value must match the value in the "<em>Process Determination</em>" <em>configuration</em></td></tr><tr><td>IM_STEP</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>/LOOPLY/STEP</td><td>You may wish to include different "Resume Looply Workflow" steps workflows at different points of the same SAP workflow. Use this field in order to achieve this. Please note that using the field is optional but it's value must match the value in the "<em>Process Determination</em>" <em>configuration</em></td></tr><tr><td>IM_WORKITEMID</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>SWW_WIID</td><td>Can be used to pass in the workitem id. This can then be used to get data from the workflow container using function SAP_WAPI_READ_CONTAINER</td></tr><tr><td>IM_REC_UNAME</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>SWF_STRING</td><td>Not actually passed to Looply. Used in here in case you want to get data for a certain SAP user</td></tr><tr><td>IM_DATA</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>SWF_STRING</td><td>Can be used to pass in any (string) data from the workflow container</td></tr><tr><td>IM_PROCESS_ID_T</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>/LOOPLY/PROCESS_ID_T</td><td>Table of process ids of the Looply workflows that will be resumed (see below for more details)</td></tr><tr><td>IM_IGNORE_ERRORS</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>FLAG</td><td>If this flag is not set, any potential errors that occur while triggering Looply will raise an exception and stop the workflow execution. Setting this flag will ignore errors so that the workflow execution continues to the next step</td></tr><tr><td>EX_DATA</td><td><span data-option="pPvjjLIx59Z2">Export</span></td><td>SWF_STRING</td><td>Use to pass back any (string) data to the Workflow container</td></tr><tr><td>EX_PROCESS_ID_T</td><td><span data-option="XVv1Ysr6YD4W">Changing</span></td><td>/LOOPLY/PROCESS_ID_T</td><td>Table of process ids of the Looply workflows that have been resumed (see below for more details). You may want to pass this to the workflow container so that you know which Looply workflows to resume at a later stage</td></tr></tbody></table>

As part of the standard task, core method /LOOPLY/WF\_OUT=>RESUME will be triggered. The method will read the ["*Process Determination*" *configuration*](/integrations/sap-integration/triggering-or-resuming-a-looply-workflow-from-sap#process-determination-configuration) based on the values of IM\_WFD\_ID, IM\_WF\_VERSION and IM\_STEP passed in above to determine a Z-function which will be called to determine which workflows to resume, as well as the data to pass  to the Looply workflow. You can create such a function by copying sample function /LOOPLY/SAMPLE\_WF\_RESUME. Based on the binding done in the workflow step above, the following parameters will be available in the function:

<table><thead><tr><th width="193">Parameter Name</th><th width="91">Type<select><option value="7PeeNLqDkkwr" label="Import" color="blue"></option><option value="pPvjjLIx59Z2" label="Export" color="blue"></option><option value="XVv1Ysr6YD4W" label="Changing" color="blue"></option></select></th><th width="187">ABAP Type</th><th>Description</th></tr></thead><tbody><tr><td>IM_WORKITEMID</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>SWW_WIID</td><td>Passed in from binding parameter</td></tr><tr><td>IM_DATA</td><td><span data-option="7PeeNLqDkkwr">Import</span></td><td>SWF_STRING</td><td>Passed in from binding parameter</td></tr><tr><td>EX_CONT_DATA</td><td><span data-option="pPvjjLIx59Z2">Export</span></td><td>SWF_STRING</td><td>Passed back to binding parameter EX_DATA</td></tr><tr><td>EX_SUBRC</td><td><span data-option="pPvjjLIx59Z2">Export</span></td><td>SYSUBRC</td><td>Assuming parameter IM_IGNORE_ERRRORS of the standard task has not been set to X via the binding, setting a subrc != 0, will cause an exception to be raised</td></tr><tr><td>EX_MESS</td><td><span data-option="pPvjjLIx59Z2">Export</span></td><td>BAPIRET2</td><td>When the exception is raised you can use this parameter to insert a certain message in the workflow log</td></tr><tr><td>CH_LOOPLY_DATA</td><td><span data-option="XVv1Ysr6YD4W">Changing</span></td><td>/LOOPLY/RESUME_DATA_T</td><td>(see below)</td></tr></tbody></table>

Table CH\_LOOPLY\_DATA can be pre-filled by the SAP workflow via the binding with one row for every row in the process id table IM\_PROCESS\_ID\_T. If IM\_PROCESS\_ID\_T is not bound to anything or left blank,   CH\_LOOPLY\_DATA will still be pre-filled with a row (with a blank process id) assuming binding parameters  IM\_RECIPIENT\_EMAIL or RECIPIENT\_UNAME are not blank. The table can be edited inside the Z-function, after which, the table will contain a row for each Looply workflow to be resumed. The table contains the following fields:

<table><thead><tr><th width="200">Field Name</th><th width="209">ABAP Type</th><th>Description</th></tr></thead><tbody><tr><td>PROCESS_ID</td><td>/LOOPLY/PROCESS_ID</td><td>The process id of the Looply workflow that will be resumed</td></tr><tr><td>RECIPIENT_UNAME</td><td>STRING</td><td>Not actually passed to Looply. Used in here in case you want to get data for a certain SAP user in your function</td></tr><tr><td>DATA</td><td>STRING</td><td>Data string we pass to Looply. You can use method /ui2/cl_json=>serialize to convert an internal table to a json string if needed</td></tr><tr><td>UTIL_DATA</td><td>STRING</td><td>Second data string in case you'd like to pass a second/separate string to Looply</td></tr><tr><td>ACTION</td><td>STRING</td><td>Can be used to pass a certain action to Looply (ie Approve, Reject, Cancel) so that you can get to it easily without having to parse DATA or UTIL_DATA</td></tr><tr><td>MESSAGE</td><td>STRING</td><td>Can be used to pass a (string) message to Looply</td></tr></tbody></table>

Filling in any of the above fields is optional and depends on what data you wish to pass to your Looply workflow. If field *PROCESS\_ID* is not filled with the process\_id of a Looply workflow at a wait state however, the workflow will not be resumed. This is not treated as an error by the framework as there may be cases in your SAP workflow where it is not possible to determine whether a Looply workflow has already been resumed or not.


# Varo/Stelo Integration

## Triggering Looply from Varo/Stelo

You can trigger a Looply workflow as part of your Varo or Stelo process by calling method **/LOOPLY/CORE=>TRIGGER\_WF** as described [here](/integrations/sap-integration/triggering-or-resuming-a-looply-workflow-from-sap).

As part of the trigger, you may wish to pass the form/app data to Looply as a JSON string. Varo users on version 320 or later can use function module /FLM/GET\_DOC\_DATA\_JSON to retrieve it. Users on earlier versions can create a copy of the function module in the Z-namespace as follows:&#x20;

Create function group ZFLM\_GET\_DOC\_DATA\_JSON with the following master program:

```abap
*******************************************************************
*   System-defined Include-files.                                 *
*******************************************************************
  INCLUDE lzflm_get_doc_data_jsontop.        " Global Declarations
  INCLUDE lzflm_get_doc_data_jsonuxx.        " Function Modules

*******************************************************************
*   User-defined Include-files (if necessary).                    *
*******************************************************************
  INCLUDE lzflm_get_doc_data_jsonsub.        " Subroutines
```

#### Function group include LZFLM\_GET\_DOC\_DATA\_JSONTOP:

```abap
FUNCTION-POOL zflm_get_doc_data_json.       "MESSAGE-ID ..

* INCLUDE LZFLM_GET_DOC_DATA_JSOND...        " Local class definition

TYPES: BEGIN OF gtyp_repeating_sf,
         subform TYPE /flm/sfs_sf,
       END OF gtyp_repeating_sf.
*
DATA: gs_fpe          TYPE /flm/fpe,
      gt_fdata        TYPE /flm/xml_tab_t,
      gt_repeating_sf TYPE TABLE OF gtyp_repeating_sf,
      gv_dd_text      TYPE flag.
```

#### Function group include LZFLM\_GET\_DOC\_DATA\_JSONSUB

```abap
*----------------------------------------------------------------------*
***INCLUDE LZFLM_GET_DOC_DATA_JSONSUB.
*----------------------------------------------------------------------*
*&---------------------------------------------------------------------*
*& Form get_json_data
*&---------------------------------------------------------------------*
*& text
*&---------------------------------------------------------------------*
*&      --> P_SF
*&      --> P_REPEATING
*&      <-- P_JSON_DATA
*&---------------------------------------------------------------------*
FORM get_json_data  USING    p_sf        TYPE string
                             p_repeating TYPE flag
                    CHANGING p_json_data TYPE string.
*
  DATA: lv_row_num      TYPE numc3,
        lv_path         TYPE string,
        lv_length       TYPE i,
        ls_fdata        TYPE /flm/xml_tab,
        ls_fdata2       TYPE /flm/xml_tab,
        ls_repeating_sf TYPE gtyp_repeating_sf,
        lv_child_num    TYPE i.
*
  IF p_repeating IS INITIAL.
*
* Non repeating subform
*
    CONCATENATE '"' p_sf '":{' INTO p_json_data.
*
* Loop around children
*
    lv_child_num = 0.
    LOOP AT gt_fdata INTO ls_fdata WHERE parent EQ p_sf.
*
      lv_child_num = lv_child_num + 1.
*
      PERFORM process_sf_child USING ls_fdata
                                     lv_child_num
                            CHANGING p_json_data.
*
    ENDLOOP.
*
    CONCATENATE: p_json_data '}' INTO p_json_data.
*
  ELSE.
*
* Repeating subform
*
    READ TABLE gt_fdata INTO ls_fdata WITH KEY name = p_sf.
    ls_repeating_sf-subform = ls_fdata-name.
    APPEND ls_repeating_sf TO gt_repeating_sf. "Save the fact that we've processed this repeating sf
    CONCATENATE '"' p_sf '":[' INTO p_json_data.
    CLEAR lv_row_num.
*
* Process one row in each loop
*
    DO.
*
      lv_row_num = lv_row_num + 1.
      lv_path = ls_fdata-path.
      lv_length = strlen( lv_path ).
      lv_length = lv_length - 3.
      CONCATENATE: lv_path+0(lv_length) lv_row_num INTO lv_path.
      READ TABLE gt_fdata WITH KEY path = lv_path TRANSPORTING NO FIELDS.
      IF sy-subrc IS NOT INITIAL.
        EXIT.
      ENDIF.
*
      IF lv_row_num EQ '001'.
        CONCATENATE: p_json_data '{' INTO p_json_data.
      ELSE.
        CONCATENATE: p_json_data ',{' INTO p_json_data.
      ENDIF.
      lv_child_num = 0.
*
      LOOP AT gt_fdata INTO ls_fdata2 WHERE parent = ls_fdata-name AND path CS lv_path.
*
        lv_child_num = lv_child_num + 1.
*
        PERFORM process_sf_child USING ls_fdata2
                                       lv_child_num
                              CHANGING p_json_data.
*
      ENDLOOP.
*
      CONCATENATE: p_json_data '}' INTO p_json_data.
*
    ENDDO.
*
    CONCATENATE: p_json_data ']' INTO p_json_data.
*
  ENDIF.
*
ENDFORM.
*&---------------------------------------------------------------------*
*& Form process_sf_child
*&---------------------------------------------------------------------*
*& text
*&---------------------------------------------------------------------*
*&      --> P_FDATA
*&      --> P_CHILD_NUM
*&      <-- P_JSON_DATA
*&---------------------------------------------------------------------*
FORM process_sf_child  USING    p_fdata     TYPE /flm/xml_tab
                                p_child_num TYPE i
                       CHANGING p_json_data TYPE string.

  DATA: lv_maxoccurs      TYPE /flm/sfs_sf_max,
        lv_json_data_temp TYPE string,
        lv_repeating      TYPE flag,
        lv_separator(1)   TYPE c,
        lv_fld_value      TYPE string.
*
  IF p_child_num GT 1.
    lv_separator = ','.
  ELSE.
    CLEAR lv_separator.
  ENDIF.
*
  SELECT SINGLE maxoccurs FROM /flm/fdd_sf INTO lv_maxoccurs
    WHERE cust_code EQ gs_fpe-ccode
     AND  ftype     EQ gs_fpe-ftype
     AND  flang     EQ gs_fpe-flang
     AND  fver      EQ gs_fpe-fver
     AND  subform   EQ p_fdata-name.
*
  IF sy-subrc IS INITIAL.
*
* Child subform
*
    READ TABLE gt_repeating_sf WITH KEY subform = p_fdata-name TRANSPORTING NO FIELDS.
    IF sy-subrc IS INITIAL.
*       This is a repeating sf we've allready processed but because it is repeating it appears more than once in gt_fdata. Don't process again
      RETURN.
    ENDIF.
*
    IF lv_maxoccurs GT '0001'.
      lv_repeating = 'X'.
    ELSE.
      CLEAR lv_repeating.
    ENDIF.
*
    PERFORM get_json_data USING    p_fdata-name
                                   lv_repeating
                          CHANGING lv_json_data_temp.
*
    CONCATENATE p_json_data lv_separator lv_json_data_temp INTO p_json_data.
*
  ELSE.
*
* Child field
*
    lv_fld_value = p_fdata-value.
    IF gv_dd_text IS NOT INITIAL.
      PERFORM format_fld_value USING p_fdata-name
                               CHANGING lv_fld_value.
    ENDIF.
    PERFORM escape_json CHANGING lv_fld_value.
    CONCATENATE: p_json_data lv_separator '"' p_fdata-name '":"' lv_fld_value '"' INTO p_json_data.
*
  ENDIF.
*
ENDFORM.
*&---------------------------------------------------------------------*
*& Form format_fld_value
*&---------------------------------------------------------------------*
*& text
*&---------------------------------------------------------------------*
*&      --> P_FDATA_NAME
*&      <-- P_FLD_VALUE
*&---------------------------------------------------------------------*
FORM format_fld_value  USING    p_fld_name
                       CHANGING p_fld_value.
*
  DATA: lv_fld_type TYPE /flm/sfs_field_type,
        lt_f4_data  TYPE /flm/sfs_form_data_t,
        ls_f4_data  TYPE /flm/form_data.
*
  SELECT SINGLE field_type FROM /flm/fdd_fld INTO lv_fld_type
    WHERE ccode      EQ gs_fpe-ccode
     AND  ftype      EQ gs_fpe-ftype
     AND  flang      EQ gs_fpe-flang
     AND  fver       EQ gs_fpe-fver
     AND  field_name EQ p_fld_name.
*
  IF sy-subrc IS INITIAL AND lv_fld_type EQ 'DROP'.
*
    CALL METHOD /flm/hds=>get_f4_entries_for_field
      EXPORTING
        im_ccode      = gs_fpe-ccode
        im_field_name = p_fld_name
        im_fstatus    = gs_fpe-fstatus
        im_flang      = gs_fpe-flang
        im_user       = sy-uname
        im_fver       = gs_fpe-fver
        im_ftype      = gs_fpe-ftype
        im_doc        = gs_fpe-document
        im_form_data  = gt_fdata
        im_prev_page  = ''
        im_excel      = 'X'
      IMPORTING
        ex_f4_data    = lt_f4_data.
*
    READ TABLE lt_f4_data INTO ls_f4_data WITH KEY name = p_fld_value.
    IF sy-subrc IS INITIAL.
      p_fld_value = ls_f4_data-value.
    ENDIF.
*
  ENDIF.
*
ENDFORM.
*&---------------------------------------------------------------------*
*& Form escape_json
*&---------------------------------------------------------------------*
*& text
*&---------------------------------------------------------------------*
*&      <-- LV_FLD_VALUE
*&---------------------------------------------------------------------*
FORM escape_json  CHANGING p_fld_value.
*
  REPLACE ALL OCCURRENCES OF `\` IN p_fld_value WITH `\\`.
  REPLACE ALL OCCURRENCES OF `"` IN p_fld_value WITH `\"`.
  REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>cr_lf          IN p_fld_value WITH `\n\n`.  "use \n\n instead of the standard \r\n here as teams cards don't understand \r\n
  REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>newline        IN p_fld_value WITH `\n\n`.  "use \n\n instead of the standard \n here as teams cards don't understand. \n\n creates two newlines in a card where idealy we'd only want one
  REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>horizontal_tab IN p_fld_value WITH `\t`.
*
ENDFORM.
```

Finally, create a new function module and add it to the function group:

#### Function module ZFLM\_GET\_DOC\_DATA\_JSON

```abap
FUNCTION zflm_get_doc_data_json.
*"----------------------------------------------------------------------
*"*"Local Interface:
*"  IMPORTING
*"     VALUE(IM_CCODE) TYPE  /FLM/CUST_CODE
*"     VALUE(IM_FTYPE) TYPE  /FLM/FTYPE_CODE
*"     VALUE(IM_FLANG) TYPE  /FLM/FLANG
*"     VALUE(IM_FVER) TYPE  /FLM/FVER
*"     VALUE(IM_ID) TYPE  /FLM/FID
*"     VALUE(IM_ID_VAR) TYPE  /FLM/ID_VAR
*"     VALUE(IM_DD_TEXT) TYPE  FLAG OPTIONAL
*"     VALUE(IM_FORM_DATA) TYPE  /FLM/XML_TAB_T OPTIONAL
*"  EXPORTING
*"     VALUE(EX_JSON_DATA) TYPE  STRING
*"----------------------------------------------------------------------
*
*-------------------------------------------------------------------------------------------------------------------------------------------*
* This function module can be used to retrieve the data from your FLM form or Stelo app in JSON format
* Setting the IM_DD_TEXT parameter to 'X' will cause the function to return the text rather than the key for drop-down fields
* If import parameter IM_FORM_DATA is left blank, the function will read the data from the content server. If data is passed in using this
* import parameter, that data will be converted isntead of the CMS data. This could be useful if you wish to format or change the data before
* it is converted to JSON
*-------------------------------------------------------------------------------------------------------------------------------------------*
*
  CLEAR: gs_fpe, gt_fdata, gt_repeating_sf.
  gv_dd_text = im_dd_text.
*
  SELECT SINGLE * INTO gs_fpe FROM /flm/fpe
    WHERE ccode  EQ im_ccode
     AND  ftype  EQ im_ftype
     AND  flang  EQ im_flang
     AND  fver   EQ im_fver
     AND  id     EQ im_id
     AND  id_var EQ im_id_var.
*
  IF im_form_data IS NOT INITIAL.
    gt_fdata = im_form_data.
  ELSE.
*
    CALL METHOD /flm/core=>get_data_from_instance
      EXPORTING
        im_form_instance = gs_fpe
      RECEIVING
        ex_form_data     = gt_fdata.
*
  ENDIF.
*
  PERFORM get_json_data USING    'DATA'
                                 ''
                        CHANGING ex_json_data.
*
  CONCATENATE: '{' ex_json_data '}' INTO ex_json_data.
*
ENDFUNCTION.
*
```

## Approving a Varo form/Stelo document from a Looply card

In order to approve a Varo form or Stelo document from a Looply card you will need to make a POST request to SAP from your Looply workfow as described [here](/integrations/sap-integration/triggering-sap-code-from-looply#post-data-to-sap). In your function, you can use function module /FLM/DOCUMENT\_PROCESS\_ACTION to process the approval or rejection. If you wish to also update the form/app data and are using Varo version 310 or earlier you can install [Varo note 254](http://support.arch-global.com/default.asp?W206) . This adds new import parameter IM\_FORM\_DATA to the function module for the new/updated data.

## Example

The following is an example of a simple use-case: when a user submits a Varo form or a Stelo app, an approver gets a Teams card with information about the request, an input field for adding comments and options to approve or reject the request. The approver action (and any added comments) are processed by the Varo  back-end. If the request is approved, the process is complete. If it is rejected, the user is notified in Teams and has the option to re-submit or cancel the request.

In the Looply *Process Determination* table, we have the following configuration:

<figure><img src="/files/JydbOcGvrFLMbyzhj8YB" alt=""><figcaption></figcaption></figure>

The workflow is triggered and resumed in the *routing user-exit* (you may also use posting adaptors to do this) depending on the routing step/FLM action:

```abap
METHOD wf_stw3 .
*
  TYPES: BEGIN OF ltyp_util_data,
           cms_doc   TYPE /flm/cms_doc,
           approver  TYPE string,
           initiator TYPE string,
         END OF ltyp_util_data.
*
  DATA: lv_process_id    TYPE /looply/process_id,
        lv_subrc         TYPE sysubrc,
        ls_mess          TYPE bapiret2,
        ls_util_data     TYPE ltyp_util_data,
        lv_util_data     TYPE string,
        lv_data          TYPE string,
        ls_fpe           TYPE /flm/fpe,
        ls_address       TYPE bapiaddr3,
        lt_return        TYPE TABLE OF bapiret2,
        ls_activity      TYPE /looply/activity,
        lv_looply_action TYPE string,
        lt_callstack     TYPE sys_callst.
*
  CONCATENATE: sy-sysid im_instance-ccode im_instance-ftype im_instance-id INTO lv_process_id SEPARATED BY '-'. "Create unique process id
  lv_looply_action = im_action.
*
* Read Looply activity table
  SELECT SINGLE * FROM /looply/activity INTO ls_activity
    WHERE scenario         EQ /looply/core=>c_scenario_vo
     AND  scenario_id      EQ im_instance-ftype
     AND  scenario_version EQ im_instance-fver
     AND  step             EQ '001'.
*
  IF im_action EQ 'S'.
*
    ex_owner = 'USER2'. "Harcoded for demonstration purposes
*
    CONCATENATE: im_instance-ccode im_instance-ftype im_instance-flang im_instance-fver im_instance-id im_instance-id_var INTO ls_util_data-cms_doc SEPARATED BY '-'.
* Get approver and initiator email address
    CALL FUNCTION 'BAPI_USER_GET_DETAIL'
      EXPORTING
        username = im_instance-finitiator
      IMPORTING
        address  = ls_address
      TABLES
        return   = lt_return.
*
    ls_util_data-initiator = ls_address-e_mail.
    CLEAR: ls_address, lt_return.
*
    CALL FUNCTION 'BAPI_USER_GET_DETAIL'
      EXPORTING
        username = ex_owner
      IMPORTING
        address  = ls_address
      TABLES
        return   = lt_return.
*
    ls_util_data-approver = ls_address-e_mail.
    lv_util_data = /ui2/cl_json=>serialize( data = ls_util_data pretty_name = /ui2/cl_json=>pretty_mode-low_case ).
*
* Get document data in json format
    CALL FUNCTION 'ZFLM_GET_DOC_DATA_JSON'
      EXPORTING
        im_ccode     = im_instance-ccode
        im_ftype     = im_instance-ftype
        im_flang     = im_instance-flang
        im_fver      = im_instance-fver
        im_id        = im_instance-id
        im_id_var    = im_instance-id_var
        im_dd_text   = 'X'
      IMPORTING
        ex_json_data = lv_data.
*
* Check whether we are re-submitting a rejected document or submitting a new one.
    SELECT SINGLE * FROM /flm/fpe INTO ls_fpe
      WHERE ccode   EQ im_instance-ccode
       AND  ftype   EQ im_instance-ftype
       AND  flang   EQ im_instance-flang
       AND  fver    EQ im_instance-fver
       AND  id      EQ im_instance-id
       AND  fstatus EQ 'R'.
*
    IF sy-subrc IS NOT INITIAL. "We're submitting a new form so trigger Looply wf.
*
      CALL METHOD /looply/core=>trigger_wf
        EXPORTING
          im_looply_wf         = ls_activity-looply_wf
          im_looply_wf_version = ls_activity-looply_wf_version
          im_scenario          = ls_activity-scenario
          im_scenario_id       = ls_activity-scenario_id
          im_scenario_version  = ls_activity-scenario_version
          im_step              = ls_activity-step
          im_process_id        = lv_process_id
          im_data              = lv_data
          im_util_data         = lv_util_data
        IMPORTING
          ex_subrc             = lv_subrc
          ex_mess              = ls_mess.
*
    ELSE. "Initiator is re-submitting a rejected form, so resume Looply wf
*
      CALL METHOD /looply/core=>resume_wf
        EXPORTING
          im_process_id       = lv_process_id
          im_data             = lv_data
          im_util_data        = lv_util_data
          im_action           = lv_looply_action
          im_scenario         = /looply/core=>c_scenario_vr
          im_scenario_id      = ls_activity-scenario_id
          im_scenario_version = ls_activity-scenario_version
          im_step             = ls_activity-step
        IMPORTING
          ex_subrc            = lv_subrc
          ex_mess             = ls_mess.
*
    ENDIF.
*
  ELSEIF im_action EQ 'X'. "Initiator has cancelled a rejected form
*
    ex_owner = 'FLM_USER'.
*
    CALL METHOD /looply/core=>resume_wf
      EXPORTING
        im_process_id       = lv_process_id
        im_action           = lv_looply_action
        im_scenario         = /looply/core=>c_scenario_vr
        im_scenario_id      = ls_activity-scenario_id
        im_scenario_version = ls_activity-scenario_version
        im_step             = ls_activity-step
      IMPORTING
        ex_subrc            = lv_subrc
        ex_mess             = ls_mess.
*
  ELSEIF im_action EQ 'A' OR im_action EQ 'R'. "Approver has approved or rejected
*
    IF im_action EQ 'A'.
      ex_owner = 'FLM_USER'.
    ELSE.
      ex_owner = im_instance-finitiator.
    ENDIF.
*
* If action happened via Fiori launchpad, resume the WF
*
    CALL FUNCTION 'SYSTEM_CALLSTACK'
      IMPORTING
        et_callstack = lt_callstack.
*
    READ TABLE lt_callstack WITH KEY progname = '/STELO/CL_APP_SERVER_DPC_EXT==CP' TRANSPORTING NO FIELDS.
    IF sy-subrc IS INITIAL.
*
      CALL METHOD /looply/core=>resume_wf
        EXPORTING
          im_process_id       = lv_process_id
          im_action           = lv_looply_action
          im_scenario         = /looply/core=>c_scenario_vr
          im_scenario_id      = ls_activity-scenario_id
          im_scenario_version = ls_activity-scenario_version
          im_step             = ls_activity-step
        IMPORTING
          ex_subrc            = lv_subrc
          ex_mess             = ls_mess.
*
    ENDIF.
*
  ENDIF.
*
ENDMETHOD.
```

The above code triggers the following workflow:

{% @arcade/embed flowId="UllRBLy6R699Zb0Nd8eI" url="<https://app.arcade.software/share/UllRBLy6R699Zb0Nd8eI>" %}

In the approver card we have the following section, containing the comments input field, action buttons and error message:

```json
{
    "type": "Container",
    "items": [
        {
            "id": "comment",
            "placeholder": "Approver Comment",
            "type": "Input.Text",
            "isMultiline": true
        },
        {
            "type": "ColumnSet",
            "columns": [
                {
                    "type": "Column",
                    "width": "stretch",
                    "items": [
                        {
                            "type": "ActionSet",
                            "actions": [
                                {
                                    "style": "positive",
                                    "type": "Action.Submit",
                                    "title": "Approve",
                                    "data": {
                                        "action": "A",
                                        "comment": ""
                                    }
                                },
                                {
                                    "type": "Action.Submit",
                                    "title": "Reject",
                                    "data": {
                                        "action": "R",
                                        "comment": ""
                                    }
                                }
                            ],
                            "horizontalAlignment": "Right"
                        }
                    ]
                }
            ]
        },
        {
            "style": "attention",
            "type": "Container",
            "$when": "${$root.payload.output.show_error}",
            "bleed": true,
            "items": [
                {
                    "weight": "Bolder",
                    "horizontalAlignment": "Center",
                    "text": "${$root.function_2.output}",
                    "type": "TextBlock",
                    "wrap": true
                }
            ]
        }
    ],
    "spacing": "ExtraLarge"
}
```

When the approver clicks on an action button on the card, the following Z-function is triggered:

```abap
FUNCTION zlooply_stw3_approve.
*"----------------------------------------------------------------------
*"*"Local Interface:
*"  IMPORTING
*"     VALUE(IM_DATA) TYPE  STRING
*"  EXPORTING
*"     VALUE(EX_SUBRC) TYPE  SYSUBRC
*"     VALUE(EX_MESS) TYPE  BAPIRET2
*"     VALUE(EX_DATA) TYPE  STRING
*"----------------------------------------------------------------------
  TYPES: BEGIN OF ltyp_data,
           cms_doc TYPE /flm/cms_doc,
           action  TYPE /flm/faction,
           comment TYPE string,
         END OF ltyp_data.
*
  DATA: ls_data        TYPE ltyp_data,
        lv_ccode       TYPE /flm/cust_code,
        lv_ftype       TYPE /flm/ftype_code,
        lv_flang       TYPE /flm/flang,
        lv_fver        TYPE /flm/fver,
        lv_fid         TYPE /flm/fid,
        lv_fid_var     TYPE /flm/id_var,
        ls_fpe         TYPE /flm/fpe,
        lt_form_data   TYPE /flm/xml_tab_t,
        ls_form_data   TYPE /flm/xml_tab,
        lt_row         TYPE TABLE OF string,
        lv_row         TYPE string,
        lv_row2        TYPE string,
        lv_length      TYPE i,
        lv_row_num     TYPE numc3,
        lv_row_max     TYPE numc3,
        lv_row_plus    TYPE numc3,
        lv_path        TYPE string,
        lv_path2       TYPE string,
        lv_path_max    TYPE string,
        lv_path_plus   TYPE string,
        ls_address     TYPE bapiaddr3,
        lt_return      TYPE TABLE OF bapiret2,
        lv_date        TYPE string,
        lv_day         TYPE string,
        lv_time        TYPE string,
        lv_hrs         TYPE numc2,
        lv_fstat_name  TYPE /flm/fstatus_name,
        lt_month_names TYPE TABLE OF T247,
        ls_month_names TYPE T247.
*
* Get data from card
*
  /ui2/cl_json=>deserialize( EXPORTING json = im_data CHANGING data = ls_data ).
*
  CALL METHOD /flm/core=>split_xdp_cms_doc
    EXPORTING
      im_cms_doc = ls_data-cms_doc
    IMPORTING
      ex_ccode   = lv_ccode
      ex_ftype   = lv_ftype
      ex_flang   = lv_flang
      ex_fver    = lv_fver
      ex_fid     = lv_fid
      ex_fid_var = lv_fid_var.
*
  SELECT SINGLE * FROM /flm/fpe INTO ls_fpe
    WHERE ccode  EQ lv_ccode
     AND  ftype  EQ lv_ftype
     AND  flang  EQ lv_flang
     AND  fver   EQ lv_fver
     AND  id     EQ lv_fid
     AND  id_var EQ lv_fid_var.
*
* Update data with approver comment (if there is one)
*
  IF ls_data-comment IS NOT INITIAL.
*
    lv_day = sy-datum+6(2).
    SHIFT lv_day LEFT DELETING LEADING '0'.
*
    CALL FUNCTION 'MONTH_NAMES_GET'
      TABLES
        month_names = lt_month_names.
*
    READ TABLE lt_month_names INTO ls_month_names WITH KEY mnr = sy-datum+4(2).
    CONCATENATE ls_month_names-LTX lv_day INTO lv_date SEPARATED BY space.
    CONCATENATE lv_date ',' INTO lv_date.
    CONCATENATE lv_date sy-datum+0(4) INTO lv_date SEPARATED BY space.
*
    lv_hrs = sy-uzeit+0(2).
    IF lv_hrs GT '12'.
      lv_hrs = lv_hrs - 12.
      CONCATENATE: lv_hrs ':' sy-uzeit+2(2) ' PM' INTO lv_time.
    ELSE.
      CONCATENATE: lv_hrs ':' sy-uzeit+2(2) ' AM' INTO lv_time.
    ENDIF.
    SHIFT lv_time LEFT DELETING LEADING '0'.
    CONCATENATE lv_date 'at' lv_time INTO lv_date SEPARATED BY space.
*
    SELECT SINGLE fstat_name FROM /flm/fstatt INTO lv_fstat_name
      WHERE spras EQ 'E'
       AND  ccode EQ ls_fpe-ccode
       AND  fstat EQ ls_fpe-fstatus.
*
    CALL FUNCTION 'BAPI_USER_GET_DETAIL'
      EXPORTING
        username = sy-uname
      IMPORTING
        address  = ls_address
      TABLES
        return   = lt_return.
*
    CALL METHOD /flm/core=>get_data_from_instance
      EXPORTING
        im_form_instance = ls_fpe
      RECEIVING
        ex_form_data     = lt_form_data.
*
    LOOP AT lt_form_data INTO ls_form_data WHERE name = 'SF_CMT'.
*
      SPLIT ls_form_data-path AT '.' INTO TABLE lt_row.
      DESCRIBE TABLE lt_row LINES lv_length.
      READ TABLE lt_row INDEX lv_length INTO lv_row.
      lv_row_num = lv_row.
      IF lv_row_num GT lv_row_max.
        lv_row_max = lv_row_num.
        lv_path_max = ls_form_data-path.
      ENDIF.
*
    ENDLOOP.
*
    READ TABLE lt_form_data INTO ls_form_data WITH KEY name = 'CMT_TEXT'.
    IF lv_row_max = '001' AND ls_form_data-value IS INITIAL. "ie no existing comments so modify emptyy row
*
      READ TABLE lt_form_data INTO ls_form_data WITH KEY name = 'CMT_DATE'.
      ls_form_data-value = lv_date.
      MODIFY lt_form_data INDEX sy-tabix FROM ls_form_data.
*
      READ TABLE lt_form_data INTO ls_form_data WITH KEY name = 'CMT_STATUS'.
      ls_form_data-value = lv_fstat_name.
      MODIFY lt_form_data INDEX sy-tabix FROM ls_form_data.
*
      READ TABLE lt_form_data INTO ls_form_data WITH KEY name = 'CMT_TEXT'.
      ls_form_data-value = ls_data-comment.
      MODIFY lt_form_data INDEX sy-tabix FROM ls_form_data.
*
      READ TABLE lt_form_data INTO ls_form_data WITH KEY name = 'CMT_USER_NAME'.
      ls_form_data-value = ls_address-fullname.
      MODIFY lt_form_data INDEX sy-tabix FROM ls_form_data.
*
    ELSE.
*
      lv_row = lv_row_max.
      lv_row_plus = lv_row_max + 1.
      lv_row2 = lv_row_plus.
      lv_path_plus = lv_path_max.
      CONCATENATE: 'SF_CMT' '.' lv_row INTO lv_path.
      CONCATENATE: 'SF_CMT' '.' lv_row2 INTO lv_path2.
      REPLACE lv_path IN lv_path_plus WITH lv_path2.
*
      READ TABLE lt_form_data INTO ls_form_data WITH KEY path = lv_path_max.
      ls_form_data-path = lv_path_plus.
      APPEND ls_form_data TO lt_form_data.
*
      CONCATENATE: lv_path_max '/CMT_DATE.001' INTO lv_path.
      READ TABLE lt_form_data INTO ls_form_data WITH KEY path = lv_path.
      CONCATENATE: lv_path_plus '/CMT_DATE.001' INTO ls_form_data-path.
      ls_form_data-value = lv_date.
      APPEND ls_form_data TO lt_form_data.
*
      CONCATENATE: lv_path_max '/CMT_STATUS.001' INTO lv_path.
      READ TABLE lt_form_data INTO ls_form_data WITH KEY path = lv_path.
      CONCATENATE: lv_path_plus '/CMT_STATUS.001' INTO ls_form_data-path.
      ls_form_data-value = lv_fstat_name.
      APPEND ls_form_data TO lt_form_data.
*
      CONCATENATE: lv_path_max '/CMT_TEXT.001' INTO lv_path.
      READ TABLE lt_form_data INTO ls_form_data WITH KEY path = lv_path.
      CONCATENATE: lv_path_plus '/CMT_TEXT.001' INTO ls_form_data-path.
      ls_form_data-value = ls_data-comment.
      APPEND ls_form_data TO lt_form_data.
*
      CONCATENATE: lv_path_max '/CMT_USER_NAME.001' INTO lv_path.
      READ TABLE lt_form_data INTO ls_form_data WITH KEY path = lv_path.
      CONCATENATE: lv_path_plus '/CMT_USER_NAME.001' INTO ls_form_data-path.
      ls_form_data-value = ls_address-fullname.
      APPEND ls_form_data TO lt_form_data.
*
    ENDIF.
*
  ENDIF.
*
* Process action
*
  CALL FUNCTION '/FLM/DOCUMENT_PROCESS_ACTION'
    EXPORTING
      im_fpe       = ls_fpe
      im_user      = sy-uname
      im_action    = ls_data-action
      im_form_data = lt_form_data
    IMPORTING
      ex_mess      = ex_mess.
*
  IF ex_mess-type EQ /flm/core=>c_mess_error.
    ex_subrc = 4.
  ENDIF.
*
ENDFUNCTION.
```


# SSL & IP address

For triggering Looply from SAP systems, please upload Looply SSL certificates to STRUST transaction.

{% file src="/files/ssjEXOlUVoNg5xWLYLgL" %}
AWS Root CA
{% endfile %}

{% file src="/files/8AJCW33NsRWpNncLnrBt" %}
api.looply.io leaf certificate
{% endfile %}

{% file src="/files/oCKU1bAVcQvAS9gI747O" %}
api.looply.io certificate chain
{% endfile %}

> AWS IP address for whitelisting Looply: 52.208.220.68


# SSO Authentication

## Azure

Login to azure portal

## SAP

Activate all nodes /sap/public/bc/sec/

Activate nodes

/sap/bc/webdynpro/sap/saml2

/sap/bc/webdynpro/sap/sec\_diag\_tool (to enable diagnostics)

Go to transaction SAML2

Enable SAML2.0 support (if not already done. Local provider option)

Provider name: <https://dev05.arch.co.uk/sap/bc/sec/oauth2/token>

Download metadata file

## Looply


# Correction Notes

## Looply 120 Notes

1\) [You get a Looply error when actioning (approving or rejecting for example) a standard Fiori app.](/integrations/sap-integration/correction-notes/note-1)


# Note 1

**Note Version:** 1\
**Created Date:** 29 Apr 2026\
**Looply Version Validity:** 110-120

{% hint style="info" %}
This correction note contains manual changes for a Looply ABAP repository object. Apply the changes in the sequence shown, then save, activate, and transport the affected object.
{% endhint %}

### Symptoms

You get a Looply error when actioning, for example approving or rejecting, a standard Fiori app.

### Cause and Pre-Requisites

Program error.

### Solution

Apply the following changes to the listed ABAP repository object.

{% hint style="warning" %}
Copy only the code inside the code blocks. Do not copy the section headings, labels, or explanatory text into the ABAP editor.
{% endhint %}

#### Object 1: Method /LOOPLY/BADI\_INBOX\_UPDT=>/IWWRK/IF\_WF\_WI\_BEFORE\_UPD\_IB\~BEFORE\_UPDATE

**Object Type:** Class Method\
**Object Name:** /LOOPLY/BADI\_INBOX\_UPDT\
**Method:** /IWWRK/IF\_WF\_WI\_BEFORE\_UPD\_IB\~BEFORE\_UPDATE\
**Transaction:** SE24

**Change 1.1**

Find block:

```abap
  APPEND ls_mess TO ct_return.
```

Replace it with:

```abap
*  APPEND ls_mess TO ct_return.                   "Note 1--
```

**Change 1.2**

Find block:

```abap
  APPEND LINES OF lt_mess TO ct_return.
```

Replace it with:

```abap
*  APPEND LINES OF lt_mess TO ct_return.          "Note 1--
```

### Final Steps

Save, activate and transport your changes through the landscape.


# Change Log / Release Notes

This page describes the different released versions of the Looply ABAP Add-On

| Version            | Release Date     | Change Log                                                                                                                                                          |
| ------------------ | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 100                | 14 November 2023 | Initial Version                                                                                                                                                     |
| 110                | 13 November 2025 | [Change Log](/integrations/sap-integration/change-log-release-notes/looply-110-change-log)                                                                          |
| 120                | 9 March 2026     | [Change Log](/integrations/sap-integration/change-log-release-notes/looply-120-change-log)                                                                          |
| 120 Service Pack 1 | 21 July 2026     | [Change Log](https://academy.looply.ai/~/revisions/RE7ZN9t3Rq8XIPCNWKil/integrations/sap-integration/change-log-release-notes/looply-120-service-pack-1-change-log) |


# Looply 110 Change Log

## 🚀 New Features

* Packaged Services: SAP Workflow, Varo, or other system events can now be utilised to trigger or resume Looply with minimal coding effort. This is achieved by delivering a number of classes that can be used as an event receiver (via transaction SWETYPV) when an event is triggered.
* Users can now set an environment profile (see [Environment Variables & Profiles](/workflows/environment-variables-and-profiles) ) when triggering a workflow. This can be passed into import parameter IM\_PROFILE of method /LOOPLY/CORE=>TRIGGER\_WF or it can be configured in the "System Settings" IMG transaction.
* New import parameter IM\_RETRY has been added to method /LOOPLY/CORE=>RESUME\_WF. If this is set and the resume fails because the workflow is not at a wait state, the resume will be attempted again (up to a maximum of 10 times) after a 1 second interval.
* New method /LOOPLY/CORE=>IS\_APP\_INSTALLED was created. This calls the [getAppUserInstallationStatus API](/api-reference/workflow-api#getappuserinstallationstatus) to check if a Teams app is installed for a certain user.&#x20;


# Looply 120 Change Log

## **🛠️ Improvements**

* The focus for this version was to improve the packaged Services module: more process specific data is now available in your Looply workflow without any extra coding effort.&#x20;

## 📜 Certification

* Looply 120 has achieved official SAP Clean Core certification, confirming that Looply aligns with SAP’s recommended architecture for S/4HANA and beyond.


# Looply 120 Service Pack 1 Change Log

## **🛠️ Improvements**

* From this version onwards, class methods replace functions as the standard approach to gather data (for Outbound or Resume) or to process an inbound call (for Inbound scenarios). If a "Class Name" is maintained in the Looply activity table for a certain scenario, the "Function Name" field will be ignored. You can create your own Z-class by implementing interface `/LOOPLY/IF_PROCESS_HANDLER`. The following methods are available:
  * `TRIGGER`: To gather data for outbound scenarios
  * `RESUME`: To gather data for resume scenarios
  * `POST`: For Inbound scenarios when a POST request is made from your Looply workflow to the `DataInSet` endpoint
  * `GET_DATA`: For Inbound scenarios when a POST request is made from your Looply workflow to the DataOutSet endpoint
  * `GET_FILE`: To return raw data, i.e. an attachment when a READ request is made to the AttSet endpoint

* A new odata endpoint `/looply/service_srv/DataOutSet` has been made available. A READ request can be made to this endpoint from Looply to gather data.

* A number of improvements were made around the Packaged Services module. The following objects are now available:<br>
  * Class `/LOOPLY/UTILS`. This contains methods to:
    * Forward a work item
    * Retrieve GOS attachment data
    * Update the Fiori Inbox of all approvers after a workitem update
    * Retrieve data regarding a certain workitem<br>

  * Class `/LOOPLY/UTIL_PROCESS_HANDLER`. This implements interface `/LOOPLY/IF_PROCESS_HANDLER` and calls class `/LOOPLY/UTILS`. It can be used out of the box without any coding, simply by configuring entries in the Activity table and making a call with a matching Scenario, Scenario Id, Scenario Version and Step from the Looply workflow. The following scenario ids must be used:

    * Forward a work item: `FORWARD_WI`
    * Retrieve GOS attachment data: `GET_GOS_ATT`
    * Update the Fiori Inbox of all approvers after a workitem update: `UPDATE_INBOX`
    * Retrieve data regarding a certain workitem: `WI_GET_WF_DETAILS`&#x20;

    <figure><img src="/files/Bh2Zx909djVm4R0Y1RhR" alt=""><figcaption></figcaption></figure>

  * Enhancement implementation `/LOOPLY/WF_RESUME_ONFORWARD`. This implements BADI `WF_WI_FORWARD` and resumes the Looply workflow when a workitem is forwarded via the Fiori inbox. To activate this implementation, modify its filter values in transaction se19.<br>

  * The workflow approval history is now available in the standard `util_data` json string sent to Looply. This contains information on the actions taken and comments added by all agents.

## **🪲 Bug Fixes**

* [Looply Note 1](/integrations/sap-integration/correction-notes/note-1) is included in this release. This corrects an error where users got a Looply error when actioning, for example approving or rejecting, a standard Fiori app.


# Okta

Integrating Looply with Okta using OAuth 2.0

This guide explains how to integrate Looply with Okta using the OAuth 2.0 Authorization Code grant.

It is intended for customers, partners, and IAM teams configuring Single Sign-On (SSO) for Looply.

The document focuses only on what Looply requires to perform OAuth-based authentication. Okta-specific implementation details may vary by organisation.

***

## Overview

Looply integrates with Okta using a user-delegated OAuth 2.0 flow.

This allows Looply to securely obtain tokens from Okta and call downstream systems (for example, SAP BTP) on behalf of the authenticated user.

Key characteristics:

* Looply does not manage user credentials
* Okta remains the identity provider
* Tokens are issued per user
* Authentication typically runs inside a Microsoft Teams popup (this does not affect OAuth configuration)

***

### Authentication Model

| **Aspect**            | **Value**                         |
| --------------------- | --------------------------------- |
| Authentication type   | User-delegated                    |
| Protocol              | OAuth 2.0                         |
| Grant type            | Authorization Code                |
| Tokens used by Looply | Access Token, Refresh Token       |
| ID Token              | Optional (not required by Looply) |

***

### OAuth 2.0 Flow Used by Looply

Looply uses the standard OAuth 2.0 Authorization Code flow:

1. User is redirected to Okta for authentication
2. Okta authenticates the user (or reuses an existing session)
3. Okta redirects back to Looply with an authorization code
4. Looply exchanges the code for tokens
5. Looply uses the Access Token to call downstream APIs
6. Looply uses the Refresh Token to renew access tokens when required

***

### Information Required to Configure Okta

#### 1. OAuth Client Configuration

An OAuth client must be created in Okta for Looply.

| **Setting**   | **Requirement**    |
| ------------- | ------------------ |
| Client type   | OAuth 2.0 client   |
| Grant type    | Authorization Code |
| Refresh token | Enabled            |
| Client secret | Required           |

***

#### 2. Redirect URI (Required)

Okta must be configured with the following redirect URI:

```
https://passport.looply.ai/custom
```

This is where Okta sends the authorization code after successful authentication.

***

#### 3. OAuth Endpoints

Looply needs access to the following Okta endpoints:

| **Endpoint**           | Purpose                                         |
| ---------------------- | ----------------------------------------------- |
| Authorization endpoint | Initiates user authentication                   |
| Token endpoint         | Exchanges authorization code and refresh tokens |
| JWKS endpoint          | Validates JWT signatures                        |

These endpoints are usually available via Okta’s discovery configuration.

***

#### 4. Scopes Required

Looply requires the following scopes:

| **Scope**       | **Purpose**                      |
| --------------- | -------------------------------- |
| openid          | Standard OAuth/OpenID scope      |
| profile         | Basic user attributes            |
| email           | Stable user identifier           |
| offline\_access | Required to issue refresh tokens |

***

#### 5. Token Requirements

| **Token**     | **Required** | **Description**                  |
| ------------- | ------------ | -------------------------------- |
| Access Token  | Yes          | JWT used to call downstream APIs |
| Refresh Token | Yes          | Used to obtain new access tokens |
| ID Token      | Optional     | Not required by Looply           |

***

#### 6. Claims Required in the Access Token

Looply relies only on standard JWT claims.

| **Claim** | **Purpose**                   |
| --------- | ----------------------------- |
| sub       | Stable unique user identifier |
| email     | Used for user correlation     |
| iss       | Token issuer                  |
| aud       | Token audience                |

No custom claims or mappings are required.

***

#### 7. User Assignment

The OAuth client should be assigned to:

* A specific Okta group, or
* A defined set of users

This controls who is allowed to authenticate into Looply.

***

### Required Configuration Details

The following details must be shared with the Looply team.

| **Item**                 | **Description**                                                       |
| ------------------------ | --------------------------------------------------------------------- |
| Client ID                | OAuth client identifier issued by Okta                                |
| Client Secret            | Secret used by Looply to exchange the authorization code              |
| Authorization Endpoint   | Okta endpoint used to initiate user authentication                    |
| Token Endpoint           | Okta endpoint used to exchange authorization codes and refresh tokens |
| JWKS Endpoint (Optional) | Okta endpoint used to validate JWT signatures                         |


# Deprecated::SSO Setup Guide: Okta → SAP Integration

Principal propagation flow Looply to SAP on-premise system

This guide walks you through configuring Single Sign-On (SSO) for Looply workflows that connect to SAP systems. By the end of this setup, your users actioning workflows via adaptive card on microsoft Teams will authenticate once through Okta and seamlessly access SAP with their own identity preserved.

***

### How It Works

> 💡 **Quick Start Guide**
>
> Depending on your existing setup, you may be able to skip certain sections:

When a user triggers a Looply workflow that needs SAP access, their identity flows securely from Okta through SAP's cloud services to your on-premise SAP system.

**What this means for you:**

* Users log in once with their existing Okta credentials
* SAP sees the actual user making the request (not a technical account)
* Full audit trail of who did what in SAP
* No custom code required—just configuration

Looply's **Token Exchange Chain** handles the complex authentication handoffs automatically. You configure the integration profiles once, and Looply orchestrates everything at runtime.

{% @mermaid/diagram content="flowchart TB
U(("User"))
TEAMS\["Microsoft Teams<br/>───────────<br/>Adaptive Card"]

OKTA\["Okta<br/>───────────<br/>Authenticate"]

LE\["Looply Engine"]
TEC\["Token Exchange Chain"]

IAS\["SAP IAS"]
XSUAA\["SAP XSUAA"]

PROXY\["Integration Suite<br/>───────────<br/>API Proxy"]

CC\["Cloud Connector<br/>───────────<br/>Principal Propagation"]

SAP\["S/4HANA <br/>───────────<br/>Execute as User"]

U -->|"1"| TEAMS
TEAMS -->|"2"| OKTA
OKTA -->|"3 · Okta token"| LE
LE --> TEC
TEC -->|"4"| IAS
IAS -->|"5"| TEC
TEC -->|"6"| XSUAA
XSUAA -->|"7"| TEC
TEC -->|"8"| PROXY
PROXY -->|"9"| CC
CC -->|"10 · X.509"| SAP

style U fill:#1b263b,stroke:#415a77,color:#e0e1dd
style TEAMS fill:#1b263b,stroke:#415a77,color:#e0e1dd
style OKTA fill:#1b263b,stroke:#415a77,color:#e0e1dd
style LE fill:#415a77,stroke:#778da9,color:#e0e1dd
style TEC fill:#415a77,stroke:#778da9,color:#e0e1dd
style IAS fill:#1b263b,stroke:#415a77,color:#e0e1dd
style XSUAA fill:#1b263b,stroke:#415a77,color:#e0e1dd
style PROXY fill:#1b263b,stroke:#415a77,color:#e0e1dd
style CC fill:#1b263b,stroke:#415a77,color:#e0e1dd
style SAP fill:#1b263b,stroke:#415a77,color:#e0e1dd
" fullWidth="false" %}

**The result:** When a user clicks an action in Teams, they authenticate once through Okta. The Looply Engine then securely propagates their identity through SAP's cloud services, so the SAP backend sees the actual user (e.g., `john.smith@company.com`)—enabling proper authorization checks, audit logging, and user-specific data access.

***

### Prerequisites

Before starting, confirm you have:

| Component      | Requirements                                                         |
| -------------- | -------------------------------------------------------------------- |
| **Okta**       | Admin access to create OAuth2 applications                           |
| **SAP IAS**    | Admin access to your Identity Authentication tenant                  |
| **SAP BTP**    | Subaccount with Authorization & Trust Management (XSUAA) entitlement |
| **SAP System** | Basis administrator access for certificate and login configuration   |
| **Users**      | Email addresses in Okta must match email addresses in SAP            |

***

### Part 1: SAP Identity Authentication Service (IAS) Setup

IAS acts as the bridge between your corporate identity provider (Okta) and SAP BTP services.

#### Step 1.1: Configure Okta as Corporate Identity Provider

> **Already configured?** If Okta is already set up as a Corporate Identity Provider in your IAS tenant, skip to Step 1.2. You can verify this in IAS Admin Console → Identity Providers → Corporate Identity Providers.

1. Log into your **IAS Admin Console**
2. Navigate to **Identity Providers** → **Corporate Identity Providers**
3. Click **Create** → Select **OpenID Connect**
4. Enter the following configuration:

| Field         | Value                                                         |
| ------------- | ------------------------------------------------------------- |
| Name          | `Okta`                                                        |
| Discovery URL | `https://<your-okta-domain>/.well-known/openid-configuration` |
| Client ID     | *(Your Okta application's Client ID)*                         |
| Client Secret | *(Your Okta application's Client Secret)*                     |

5. Under **Subject Name Identifier**, select **email**
6. Click **Save** and set to **Active**

#### Step 1.2: Create OIDC Application for Token Exchange

> **Already have an OIDC application?** If you have an existing IAS application configured for JWT Bearer grants, you can reuse it. Ensure it has Okta enabled under Trusted Identity Providers and note the Client ID/Secret for Looply.

{% hint style="warning" %}
When a custom IAS tenant is linked/trusted by a SAP BTP sub account, an OIDC application with the following ID: **XSUAA\_\<BTP sub account ID>** is automatically created by SAP BTP provisioning service. If this application already exists, Skip the Step 1.2
{% endhint %}

**Option A: Use the Auto-Created XSUAA Application (Recommended)**

1. In IAS Admin Console, go to **Applications & Resources → Applications**
2. Find and select `XSUAA_<your-subaccount-id>`
3. Go to **Conditional Authentication**
4. Under **Default Authenticating Identity Provider**, select **Okta IDP (Corporate IDP)** from the dropdown
5. Click **Save**
6. Go to **Client Authentication**
7. Under **Secrets**, click **+Add** to create a client secret
8. Note the **Client ID** and **Client Secret** for Looply configuration
9. Click **Save**

> ⚠️ **Can't modify the Default Identity Provider?** If the auto-created application is managed by another team or you cannot change the default IdP, use Option B below.

**Option B: Create a Dedicated Looply Application**

Use this approach if you cannot modify the auto-created XSUAA application.

1. In IAS Admin Console, go to **Applications & Resources → Applications**
2. Click **Create** and configure:

| Setting      | Value                                              |
| ------------ | -------------------------------------------------- |
| Display Name | `looply_okta_ias`                                  |
| Home URL     | *(leave blank or enter your Looply workspace URL)* |

3. After creation, go to **Parent Application**
4. Click **Edit** and select the `XSUAA_<btp subaccount-id>` application as the parent
5. Click **Save**
6. Go to **Conditional Authentication**
7. Under **Default Authenticating Identity Provider**, select **Okta**
8. Uncheck **Allow Identity Authentication Users Log On** (optional—ensures only Okta users can authenticate)
9. Click **Save**
10. Go to **Client Authentication**
11. Under **Secrets**, click **+Add** to create a client secret
12. API Access: Select **openid**
13. Note the **Client ID** and **Client Secret**
14. Click **Save**

**Required Application Settings Checklist**

Regardless of which option you choose, verify these settings:

| Setting                   | Location                        | Required Value                                |
| ------------------------- | ------------------------------- | --------------------------------------------- |
| Default Identity Provider | Conditional Authentication      | **Okta**                                      |
| Client Secret             | Client Authentication → Secrets | Generated and saved                           |
| Subject Name Identifier   | Subject Name Identifier         | **Email** (should inherit from Corporate IdP) |

#### What to Provide to Looply

After completing IAS setup, share these values with your Looply team:

```yaml
ias:
  tenant_url: https://<your-ias-tenant>.accounts.ondemand.com
  token_url: https://<your-ias-tenant>.accounts.ondemand.com/oauth2/token
  client_id: <from-client-authentication>
  client_secret: [provide securely]
```

***

### Part 2: SAP XSUAA Setup

XSUAA (Authorization and Trust Management Service) issues the final OAuth2 token that grants access to SAP APIs (including proxies exposed via SAP Integration suite).

#### Step 2.1: Trust IAS in Your BTP Subaccount

> **Already trusted?** If IAS is already configured as a trusted identity provider in your BTP subaccount, skip to [Step 2.2](https://claude.ai/chat/9316e1e4-f8ef-4972-9d17-c411ef9940f5#step-22-create-xsuaa-service-instance). Check this in BTP Cockpit → Subaccount → Security → Trust Configuration.

1. Log into **SAP BTP Cockpit**
2. Navigate to your Subaccount → **Security** → **Trust Configuration**
3. Click **Establish Trust**
4. Select custom SAP IAS tenant and verify if the custom IAS tenant appears under Custom Identity Provider fro Applications

#### Step 2.2: Create XSUAA Service Instance

> **Already have an XSUAA instance?** If you have an existing XSUAA service instance with the `apiaccess` plan and `jwt-bearer` grant type enabled, you can reuse it. Skip to [Step 2.3](https://claude.ai/chat/9316e1e4-f8ef-4972-9d17-c411ef9940f5#step-23-create-service-key) to generate a new service key for Looply.

{% hint style="info" %}
If you are unable to create **Authorization and Trust service instance (XSUAA)** without **apiaccess** plan, please check if **cloud foundry** is **enabled** in the BTP subaccount. Also verify if you have the relevant **entitlements** to the required plan assigned.
{% endhint %}

1. In BTP Cockpit, go to **Service Marketplace**
2. Search for **Authorization and Trust Management Service**
3. Click **Create** and configure:

| Setting       | Value                          |
| ------------- | ------------------------------ |
| Plan          | `apiaccess`                    |
| Instance Name | `looply-principal-propagation` |

#### Step 2.3: Create Service Key

1. After the instance is created, click **Create Service Key**
2. Name: `looply-key`
3. Copy the generated service key JSON

#### What to Provide to Looply

Extract these values from your service key:

```yaml
xsuaa:
  token_url: <url from service key>/oauth/token
  client_id: <clientid from service key>
  client_secret: [provide securely]
```

***

### Part 3: SAP On-Premise Configuration

These steps configure your SAP system to accept certificate-based authentication from Cloud Connector.

> **Already using Principal Propagation?** If your SAP system is already configured for certificate-based authentication with Cloud Connector (e.g., for other BTP integrations), you may only need to verify the existing configuration. Skip to [Step 3.4](https://claude.ai/chat/9316e1e4-f8ef-4972-9d17-c411ef9940f5#step-34-verify-user-email-addresses) to ensure user emails are properly mapped.

#### Step 3.1: Import Cloud Connector CA Certificate (STRUST)

The Cloud Connector generates certificates for each user. SAP must trust this certificate authority.

1. Obtain the CA certificate from Cloud Connector:
   * Cloud Connector Admin → **Principal Propagation** → Export **Local CA Certificate**
2. In SAP, run transaction **STRUST**
3. Navigate to **SSL Server Standard** → **Certificate List**
4. Click **Import Certificate** and upload the Cloud Connector CA
5. Add to the certificate list and **Save**

#### Step 3.2: Import Cloud Connector System Certificate (STRUST)

* Obtain the System certificate from Cloud Connector:
  * Cloud Connector Admin → **Principal Propagation** → Export **System Certificate**
* In SAP, run transaction **STRUST**
* Navigate to **SSL Server Standard** → **Certificate List**
* Click **Import Certificate** and upload the Cloud Connector CA
* Add to the certificate list and **Save**

#### Step 3.3: Configure Certificate-Based Login (RZ10)

1. Run transaction **RZ10**
2. Edit your **instance profile**
3. Add these parameters:

```
icm/HTTPS/trust_client_with_issuer = "*CN=<Your-Cloud-Connector-CA-Name>*"
icm/HTTPS/verify_client = 1
login/certificate_mapping_rulebased = 1
```

4. Save and activate the profile
5. **Restart ICM** (transaction SMICM → Administration → ICM → Restart)

#### Step 3.4: Configure Certificate Mapping (CERTRULE)

This tells SAP how to map the certificate's CN (email) to a SAP user.

1. Run transaction **CERTRULE**
2. Import a sample certificate (from Cloud Connector for testing)
3. Create a new mapping rule:

| Setting         | Value                                 |
| --------------- | ------------------------------------- |
| Rule Attribute  | `Issuer`                              |
| Condition       | `*CN=<Your-Cloud-Connector-CA-Name>*` |
| Login Attribute | `Subject` → `CN`                      |
| Login Type      | `E-Mail (table USREMAIL)`             |

#### Step 3.5: Verify User Email Addresses

**This is critical.** For principal propagation to work, each SAP user must have an email address that **exactly matches** their Okta email.

1. Run transaction **SU01** to check individual users
2. Verify the **E-Mail** field (Address tab) matches the Okta email
3. For bulk verification, use **SE16** to query table **USREMAIL**

> **Note:** Email matching may be case-sensitive depending on your configuration. Ensure consistent casing between Okta and SAP.

***

### Configuration Summary

Once you've completed all steps, gather this information for your Looply implementation team:

| Component           | Information Needed                                       |
| ------------------- | -------------------------------------------------------- |
| **IAS**             | Tenant URL, Token URL, Client ID, Client Secret          |
| **XSUAA**           | Token URL, Client ID, Client Secret (from service key)   |
| **Cloud Connector** | CA Common Name, Subject Pattern                          |
| **User Mapping**    | Confirmation that user emails match between Okta and SAP |

***

### Testing Your Setup

Use this checklist to verify each component:

#### IAS Token Exchange

* [ ] IAS accepts tokens from Okta
* [ ] IAS returns access tokens containing user identity

#### XSUAA Token Exchange

* [ ] XSUAA accepts tokens from IAS
* [ ] XSUAA returns OAuth2 tokens with user principal

#### SAP System

* [ ] Certificate validated successfully (check SMICM trace)
* [ ] Certificate mapped to correct SAP user (check SM21 logs)
* [ ] API executes with proper user authorization
* [ ] Audit log shows the actual user (not a technical account

***

### Next Steps

After completing this setup:

1. Provide the configuration values to your Looply implementation team
2. We'll configure the Token Exchange Chain profiles in your Looply workspace
3. Test with a sample workflow to verify end-to-end connectivity
4. Roll out to your users

***

*Need help? Contact your Looply implementation team or reach out to <support@looply.ai>*


# Troubleshooting & FAQ

This section is organized by the stage of the authentication flow where issues typically occur.

***

### Stage 1: Okta Authentication

**Q: User sees "Unable to sign in" or gets redirected back to login**

**Symptoms:**

* User enters Okta credentials but login fails
* Browser redirects back to login page without error
* Okta shows successful login but Looply doesn't receive the token

**Possible Causes & Solutions:**

| Cause                    | How to Verify                                 | Solution                                                       |
| ------------------------ | --------------------------------------------- | -------------------------------------------------------------- |
| Redirect URI mismatch    | Check Okta app config → Sign-in redirect URIs | Ensure `https://passport.looply.ai/callback` is listed exactly |
| User not assigned to app | Okta Admin → Applications → Assignments       | Assign user or group to the Looply application                 |
| Scopes not configured    | Check Okta authorization server settings      | Ensure `openid`, `email`, `profile` scopes are allowed         |
| Token claims missing     | Decode token at jwt.io                        | Verify `email` claim is present in the ID token                |

**Diagnostic Steps:**

1. In Okta Admin Console, go to **Reports → System Log**
2. Filter by the user's email and time of login attempt
3. Look for authentication events and any error codes

***

**Q: Okta token doesn't contain the expected email claim**

**Symptoms:**

* Authentication succeeds but downstream token exchanges fail
* IAS rejects the token with "subject not found" or similar

**Solution:**

1. Go to Okta Admin → **Security → API → Authorization Servers**
2. Select your authorization server (or `default`)
3. Go to **Claims** tab
4. Verify an `email` claim exists with:
   * Include in token type: **ID Token** (or Always)
   * Value: `user.email`

***

### Stage 2: IAS Token Exchange

**Q: Error "invalid\_grant" when exchanging Okta token at IAS**

**Symptoms:**

* Looply logs show `invalid_grant` error from IAS token endpoint
* Token exchange fails at the first hop (Okta → IAS)

**Possible Causes & Solutions:**

| Cause                                     | How to Verify                                                 | Solution                                                    |
| ----------------------------------------- | ------------------------------------------------------------- | ----------------------------------------------------------- |
| Okta not configured as Corporate IdP      | IAS Admin → Identity Providers → Corporate Identity Providers | Complete Step 1.1 in setup guide                            |
| Wrong Client ID/Secret in IAS             | Compare IAS Corporate IdP config with Okta app                | Update credentials in IAS to match Okta application         |
| JWT issuer mismatch                       | Decode Okta token → check `iss` claim                         | Ensure IAS discovery URL matches the Okta issuer exactly    |
| Token expired                             | Check `exp` claim in JWT                                      | Tokens typically expire in 1 hour; ensure clocks are synced |
| Corporate IdP not enabled for application | IAS → Applications → Your App → Trust                         | Enable Okta under "Trusted Identity Providers"              |

**Diagnostic Steps:**

1. In IAS Admin Console, go to **Monitoring & Reporting → Troubleshooting Logs**
2. Enable logging for your application
3. Retry the authentication and search logs for "token" or "jwt"
4. Check the detailed error message in the log entry

***

**Q: Error "invalid\_client" from IAS**

**Symptoms:**

* IAS rejects the token exchange request immediately
* Error occurs before JWT validation

**Possible Causes & Solutions:**

| Cause                                     | Solution                                                                  |
| ----------------------------------------- | ------------------------------------------------------------------------- |
| Wrong Client ID for IAS application       | Verify Client ID in IAS → Applications → Your App → Client Authentication |
| Wrong Client Secret                       | Regenerate secret in IAS and update in Looply                             |
| Application not configured for JWT Bearer | Ensure Grant Type includes `JWT Bearer` in IAS application settings       |

***

**Q: IAS returns token but user identity is wrong or missing**

**Symptoms:**

* Token exchange succeeds but downstream services don't recognize the user
* XSUAA or SAP shows wrong user or "anonymous"

**Solution:**

1. In IAS, go to your **Corporate Identity Provider** (Okta)
2. Check **Subject Name Identifier** setting
3. Ensure it's set to `email` (or the attribute that matches your SAP users)
4. Under **Enrich Token Claims**, verify the correct claims are being passed through

***

### Stage 3: XSUAA Token Exchange

**Q: Error "invalid\_token" or "unauthorized" from XSUAA**

**Symptoms:**

* IAS token exchange succeeded, but XSUAA rejects the IAS token
* Looply logs show 401 or `invalid_token` from XSUAA endpoint

**Possible Causes & Solutions:**

| Cause                                   | How to Verify                                     | Solution                                                          |
| --------------------------------------- | ------------------------------------------------- | ----------------------------------------------------------------- |
| IAS not trusted in BTP subaccount       | BTP Cockpit → Security → Trust Configuration      | Add IAS as trusted identity provider (Step 2.1)                   |
| XSUAA instance missing jwt-bearer grant | Check xs-security.json or service instance config | Recreate instance with correct grant types                        |
| Token audience mismatch                 | Decode IAS token → check `aud` claim              | Ensure IAS application is configured to include XSUAA in audience |
| Service key credentials wrong           | Compare service key JSON with Looply config       | Update Client ID/Secret from a fresh service key                  |

**Diagnostic Steps:**

1. In BTP Cockpit, go to your subaccount → **Services → Instances**
2. Find your XSUAA instance and view the service key
3. Verify the `url`, `clientid`, and `clientsecret` match Looply's configuration

***

**Q: Error "insufficient\_scope" from XSUAA**

**Symptoms:**

* Token exchange succeeds but API calls fail with scope errors
* XSUAA token doesn't include expected scopes

**Solution:**

1. Review your `xs-security.json` configuration
2. Ensure required scopes are defined
3. Verify the IAS token includes the necessary claims for scope mapping
4. Check role collections are assigned to users in BTP Cockpit

***

### Stage 4: Cloud Connector & SAP Backend

**Q: Error "SSL handshake failed" or certificate errors in Cloud Connector logs**

**Symptoms:**

* Cloud Connector logs show SSL/TLS errors
* Connection to SAP backend fails before authentication

**Possible Causes & Solutions:**

| Cause                                  | How to Verify                                                     | Solution                                          |
| -------------------------------------- | ----------------------------------------------------------------- | ------------------------------------------------- |
| System certificate not generated       | Cloud Connector → Configuration → On Premise → System Certificate | Generate a self-signed system certificate         |
| CA certificate not created             | Cloud Connector → Configuration → On Premise → CA Certificate     | Generate CA certificate for principal propagation |
| SAP doesn't trust Cloud Connector cert | Check STRUST in SAP                                               | Import Cloud Connector CA certificate (Step 3.1)  |

***

**Q: SAP returns HTTP 401 or 403 even though certificates are configured**

**Symptoms:**

* Cloud Connector connects successfully
* SAP receives the request but rejects it with 401/403
* SMICM trace shows "intermediary is NOT trusted"

**Possible Causes & Solutions:**

| Cause                                    | How to Verify                                                | Solution                                                                           |
| ---------------------------------------- | ------------------------------------------------------------ | ---------------------------------------------------------------------------------- |
| ICM trust parameters not set             | RZ10 → Check profile parameters                              | Add `icm/HTTPS/trust_client_with_issuer` and `icm/HTTPS/trust_client_with_subject` |
| Parameter values don't match certificate | Compare RZ10 values with Cloud Connector certificate details | Copy exact Issuer and Subject DN from Cloud Connector system certificate           |
| ICM not restarted                        | Check SMICM parameter display                                | Restart ICM via SMICM → Administration → ICM → Exit Hard → Global                  |
| Profile not activated                    | RZ10 → Check profile status                                  | Save and activate profile, then restart ICM                                        |

**Diagnostic Steps:**

1. Run transaction **SMICM**
2. Go to **Goto → Trace File → Display End**
3. Look for entries containing "trust" or "certificate"
4. Check for messages like "intermediary is NOT trusted"

**Example SMICM trace showing the issue:**

```
HttpModIsReverseProxyTrustworthy: no trust relationship to intermediary specified
HttpModGetDefRules: intermediary is NOT trusted -> remove SSL header fields
```

***

**Q: SAP shows "User not found" or login fails after certificate validation**

**Symptoms:**

* Certificate is accepted (no SSL errors)
* But SAP cannot map certificate to a user
* SM21 system log shows login failures

**Possible Causes & Solutions:**

| Cause                               | How to Verify                        | Solution                                             |
| ----------------------------------- | ------------------------------------ | ---------------------------------------------------- |
| CERTRULE not configured             | Run CERTRULE transaction             | Create mapping rule (Step 3.3)                       |
| Rule-based mapping not enabled      | Check RZ10 parameters                | Set `login/certificate_mapping_rulebased = 1`        |
| Email mismatch                      | Compare Okta email with SAP USREMAIL | Ensure emails match exactly (case-sensitive)         |
| User missing email in SAP           | SU01 → User → Address tab            | Add email address matching Okta                      |
| Wrong certificate attribute in rule | CERTRULE → View rule details         | Ensure rule extracts CN and maps to Email login type |

**Diagnostic Steps:**

1. Run transaction **SM21** (System Log)
2. Filter by time of failed login
3. Look for certificate-related messages
4. Run **SE16** → Table **USREMAIL** to verify user email mappings

***

**Q: Authentication works but SAP shows wrong user or technical user**

**Symptoms:**

* API calls succeed
* But audit log shows a technical user instead of the actual user
* Authorization checks fail because wrong user context

**Possible Causes & Solutions:**

| Cause                                            | Solution                                                                                    |
| ------------------------------------------------ | ------------------------------------------------------------------------------------------- |
| Principal Propagation not enabled on destination | In BTP Cockpit, edit destination → Set Authentication to `PrincipalPropagation`             |
| Cloud Connector Access Control misconfigured     | Cloud Connector → Access Control → Edit mapping → Set Principal Type to `X.509 Certificate` |
| Subject pattern wrong                            | Cloud Connector → Principal Propagation → Verify Subject Pattern is `CN=${email}`           |

***

### General Diagnostics

**Q: Where can I find logs for each component?**

| Component           | Where to Find Logs                                             |
| ------------------- | -------------------------------------------------------------- |
| **Okta**            | Admin Console → Reports → System Log                           |
| **IAS**             | Admin Console → Monitoring & Reporting → Troubleshooting Logs  |
| **XSUAA/BTP**       | BTP Cockpit → Subaccount → Logs (if enabled)                   |
| **Cloud Connector** | Admin UI → Logs; or `ljs_trace.log` / `scc_core.trc` files     |
| **SAP ABAP**        | SMICM (ICM trace), SM21 (System log), STAD (Workload analysis) |
| **Looply**          | Contact Looply support for workflow execution logs             |

***

**Q: How do I enable detailed tracing in SAP ICM?**

**Steps:**

1. Run transaction **SMICM**
2. Go to **Goto → Trace Level → Set Trace Level**
3. Set level to **2** or **3** (3 = most detailed)
4. Reproduce the issue
5. Go to **Goto → Trace File → Display End**
6. Search for relevant entries (certificate, trust, SSL)

> ⚠️ **Important:** Reset trace level to 1 after debugging to avoid performance impact.

***

**Q: How can I test the token exchange chain step-by-step?**

Contact your Looply implementation team. We can provide:

* A Postman collection for testing each token exchange hop individually
* Step-by-step guidance on validating tokens at each stage
* Help decoding and inspecting JWT tokens at each hop

***

**Q: How do I decode and inspect a JWT token?**

**Option 1: Online (for non-sensitive tokens only)**

* Go to [jwt.io](https://jwt.io/)
* Paste the token to see header, payload, and signature

**Option 2: Command line**

```bash
# Decode the payload (middle section of the JWT)
echo "<middle-part-of-jwt>" | base64 -d | jq .
```

**Key claims to check:**

| Claim   | What to Verify                            |
| ------- | ----------------------------------------- |
| `iss`   | Issuer matches expected identity provider |
| `aud`   | Audience includes the target service      |
| `sub`   | Subject contains the user identifier      |
| `email` | Email matches SAP user's email            |
| `exp`   | Token hasn't expired                      |

***

### Still Need Help?

If you've worked through the relevant FAQ items and are still experiencing issues:

1. **Gather diagnostic information:**
   * Screenshot or copy of the error message
   * Relevant logs from the component where the failure occurs
   * Time of the failed request (for log correlation)
2. **Contact Looply support:**
   * Email: <support@looply.ai>
   * Include the diagnostic information above
   * Reference this FAQ and which items you've already checked
3. **For SAP-specific issues:**
   * Engage your SAP Basis team with the SMICM/SM21 traces
   * Reference SAP Note 2052899 for trusted proxy configuration


# Building Apps

Tailor and deploy MS Teams apps within your landscape, connecting processes and ensuring timely notifications with Looply

## Introduction

After integrating your organization's Microsoft tenant with Looply, unlock the full potential of customization by leveraging the Looply App Manager. This powerful tool allows you to effortlessly create custom Microsoft Teams apps that seamlessly integrate into your Teams landscape. These tailor-made apps serve as the bridge connecting your meticulously crafted workflows with your users, ensuring a cohesive and efficient collaboration experience within Microsoft Teams.

A Microsoft Teams app, within the Looply ecosystem, stands as a versatile extension that amplifies the collaborative capabilities of your Teams environment. These apps, crafted using the Looply App Manager, serve as specialized tools designed to augment your organization's workflows and communication within Microsoft Teams.

## Creating a new app

You can begin creating a new MS Team app by selecting the App Manager and clicking the **Create** button.&#x20;

<figure><img src="/files/Xd2wjW7lAryhPloB4FGd" alt=""><figcaption></figcaption></figure>

### Basic Information

Start creating your custom MS Teams app by adding some basic information that will describe your app to your Teams users.&#x20;

You can add a short name, description, and a longer description for your Teams app. There is also space to add links to your organisation's privacy policy and terms of use documents.&#x20;

<figure><img src="/files/vo6GkIRmRpQ8sPdAR667" alt=""><figcaption></figcaption></figure>

### Adding Custom Branding

You can use the **Branding** tab to customise the branding of your Teams app to match your organisation.&#x20;

The branding options supported by Looply are:&#x20;

* **Color Icon** - 192x192px full-color image to be displayed as the main logo of your app
* **Outline Icon** - 36x36px (recommended white or transparent) icon to be used within the Teams sidebar
* **Accent Color** - a single solid color to be used as the accent and displayed within messages

<figure><img src="/files/esbZkxsXAdbvuTsZ6iS6" alt=""><figcaption></figcaption></figure>

### Configure App Tabs

Use the **Tabs** section to add up to 3 custom links; accessible for your users from within your app in Teams as individual tabs.&#x20;

These can be used to attach useful information pages or links relevant to your users.&#x20;

By default, all Teams apps will feature tabs for Chat, About and Notifications Centre - these cannot be modified or removed. You can add a new custom tab by clicking the **New Tab** button and, entering a display name and URL for your custom tab link.&#x20;

<figure><img src="/files/MzMbIzY5ZkzLEyntIyUI" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
**Note:** Make sure to include any custom URLs you want your Teams app to link to within your app's domain whitelist located in the **Settings** section.&#x20;

Attempting to attach links to external URLs not included in your whitelist will result in an error.&#x20;
{% endhint %}

### Handling App Events

You can use the **App Events** tab to define how your Teams app should react to specific user events.&#x20;

<figure><img src="/files/RGv2iSwudx23G6xnVcWT" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
**Note:** We currently only support **Welcome** and **Fallback** events within your Teams apps.&#x20;
{% endhint %}

#### Welcome Events

A welcome event occurs when a user installs your Teams app for the first time. When this happens, you might want to send them a customised onboarding message and/or link to help them get started.

Configure your welcome event by entering a welcome message title, body, and link which will be displayed as a button link within Teams.&#x20;

Welcome messages support up to 140 characters and can be personalised with your user's first or last name - which can be inserted by clicking on either the **First Name** or **Last Name** icon buttons while your cursor is active in the text field.&#x20;

#### Fallback Events

A fallback event can be triggered when your user attempts to perform an action that is not permitted by your Teams app.&#x20;

For example, a fallback event would be triggered if your user sends a chat message to your Teams app that is not recognised.&#x20;

You can enter a message that will be sent to the user (up to 140 characters) when this exception occurs.&#x20;

## Saving your changes

Once you have added or modified some information for your Teams app, you can save your changes by clicking the **Save** button.&#x20;

Saving your changes for the first time will create a new Teams app within your workspace on Looply. Subsequently, saving changes after this point will update the existing app.&#x20;

A status for your Teams app will be determined or redetermined every time you save your app. See App Statuses to learn more about the different statuses of your app.&#x20;

### Version Control

Enjoy the assurance of version control as you create Microsoft Teams apps with Looply, ensuring that each iteration is seamlessly tracked and documented for a transparent and organized development process.

#### Creating New Versions

When you create an app for the first time, it will be assigned as the first version. You can modify the current version of your app as many times as you wish until that version has been deployed.&#x20;

Once a version has been deployed within your landscape it becomes locked from further changes to prevent potentially system-breaking changes from accidentally going live. If you attempt to make changes after this point and click Save, your Teams app will **automatically create a new version** for you and will not be live until that version is deployed.&#x20;

Additionally, you can choose to manually create a new version of your app at any time by clicking the dropdown arrow located on the Save button and selecting the **Save (New Version)** option.&#x20;

<figure><img src="/files/5zMjQPzcxyUP9HPzdH7x" alt=""><figcaption></figcaption></figure>

#### Viewing Version History

The **Version History** tab can be used to view previously created versions of your Teams app. Users can select previous versions by clicking on the version number of a previous version to load it. T

his can be useful for restoring older versions, by selecting the version you want to restore and then saving/re-deploying that version.&#x20;


# Deploying apps to Teams App catalog


# Looply Dashboard

Deploy Teams apps to your organisations Microsoft landscape with ease using Looply

Once you have created your Teams app using Looply, you'll need to deploy it to your organisations Microsoft landscape in order to make it available for your users to install and unlock remote app management.&#x20;

### Quick Deployment

{% hint style="info" %}
**Note:** You will need to integrate a Microsoft account from your organisation that has the appropriate privileges required to publish a new app to your landscape with Looply before continuing.&#x20;
{% endhint %}

Versions of your Teams app can be seamlessly deployed to your Microsoft organisation once they have been populated with all required information and in the **Ready** status.&#x20;

To deploy your app, Go to **App Manager** -> click the **Deploy** button located at the top of the Create/Edit app page.&#x20;

<figure><img src="/files/KjIWr9omuAXqcUGJv7v7" alt=""><figcaption></figcaption></figure>

You'll need to confirm which Microsoft integrated account you wish to use for deployment by selecting it and clicking **Deploy** again in the side panel.&#x20;

If you can't see the correct Microsoft account within the side panel window, we recommend disconnecting and re-connecting your integration.&#x20;

Once your deployment is complete, you can verify it has worked correctly by going to Microsoft Teams and viewing the integrated app catalog.&#x20;

### Forcing Admin Approval

Looply supports the ability to force your custom Teams app to require approval from an organisation administrator when deployed in your Microsoft 365 landscape.

This feature ensures that every app package sent from Looply must be explicitly reviewed and approved before being made available to end users.

This safeguard is especially useful in environments where:

* You need a change-control step before allowing production use
* Your organisation follows strict IT governance
* A separate security or compliance team must authorise all internal apps
* You want to prevent unapproved versions from being distributed automatically

You can enable this functionality by toggling the **App Requires Review** switch on from within the **Settings** tab of the Teams app you are creating or editing.&#x20;

<figure><img src="/files/u1qSxtMpSwyUgIdTzUKH" alt=""><figcaption></figcaption></figure>

When this toggle is enabled, Looply instructs Microsoft Graph to deploy the app using the **requiresReview=true** flag.

This means the app will not be published directly to the Teams app catalog — instead, it is submitted for admin review inside your Microsoft tenant.

### Approving Custom Apps in the Teams Admin Center

When Looply deploys an app with App Requires Review enabled, the app will appear in Microsoft Teams as “Needs approval”. A Teams administrator must approve it before it becomes available to users.

Follow these steps:

1\. Open the Teams Admin Center

Go to: <https://admin.teams.microsoft.com>

Sign in with one of the following roles:

* Teams Administrator
* Teams App Administrator
* Global Administrator

2\. Go to Manage Apps

In the left menu: Teams apps → Manage apps

3\. Locate the Pending App

Use one of the following:

* Search for the app name

4\. Publish or Reject

Select the app → choose Publish (or Reject).

Publish action deploys the app to your Organisation’s, making it available for installation and use by Looply workflows.

5\. App Goes Live

Once approved:

* The app moves to the Published list
* Users can install it (subject to policies)
* Looply can use it for adaptive cards and workflow actions

#### What Happens If “Requires Review” Is Disabled?

If you turn off the App Requires Review toggle in Looply:

* The app will be published immediately to the Organisation catalog
* No admin approval step is needed
* Only the standard Teams permission policies control user access
* Deployment is instant, suitable for rapid development environments


# Manual Installation

Users can also perform a manual installation of their app using the ZIP file available when creating or editing an app. This can be useful for testing purposes, or if you explicitly do not want your app to be available in the Teams app catalog for all users.&#x20;

#### Downloading ZIP

You can download your Teams app as a ZIP file by clicking the **Download ZIP** button.&#x20;

These files will contain:&#x20;

* Color Icon - PNG file for your chosen color icon image
* Outline Icon - PNG file for your chosen outline icon image
* Manifest - Generated manifest JSON file containing the details of your app

#### Uploading ZIP in Teams

Once you have the ZIP file of your app, you can move to Microsoft Teams to upload and install it.&#x20;

From within Microsoft Teams:&#x20;

1. Select **Apps** from the left sidebar
2. Choose **Manage Your Apps**
3. Click **Upload an app**
4. Select **Upload a custom app**
5. Choose your ZIP file&#x20;

Your custom Teams app should now be successfully uploaded and installed within your Microsoft Teams client.&#x20;


# Installing Looply Apps

Remotely manage your Teams app users in Looply

Looply respects the need for central control. Organisational administrators can remotely install/uninstall/update the app for users, ensuring uniformity in app versions and features across the board.

{% hint style="info" %}
**Note:** This feature is only available for Looply **administrators** and requires your Teams app to have been deployed on your landscape.&#x20;
{% endhint %}

#### Individual Users

* Go to **App Manager** from the side navigation menu
* Click on the App tile to deploy the app to the required users
* Go to **User Manager** tab
* Search for the user using their email address
* Click Install

<figure><img src="/files/bTiNrjqRBxuykE8jKcn1" alt=""><figcaption><p>App manager</p></figcaption></figure>

{% hint style="info" %}
Note: App can only be installed to users after the app has been deployed to the organizational app catalog. Please refer to deploying apps to Teams app catalog for details.&#x20;
{% endhint %}

You can remotely manage your Teams app for individual users via the **User Manager** tab when creating or editing an app. From here, you can view all of the users within your Microsoft organisation and each of their installation statuses of your app.&#x20;

<figure><img src="/files/rZihzb12S7uWxbAmrHrr" alt=""><figcaption><p>User Manager </p></figcaption></figure>

You can install or uninstall the selected version of your app for a specific user by clicking **Install** or **Uninstall** beside the user's account information within the table. Additionally, if the user has an older version of the app installed - you can force an update to the current selected version.&#x20;

<figure><img src="/files/zzSFTfDJSFpDDNEG33DF" alt=""><figcaption></figcaption></figure>

#### Installation History

All details about who has installed your Teams app are logged under the **Installation History** tab. From here, you'll be able to see a list of users, the date of installation, the version installed, and the installation type (personal, group chat, or channel).&#x20;

<figure><img src="/files/AosVqaoOB3e3PtzeX6AP" alt=""><figcaption></figcaption></figure>


# Bulk Rollout

## Deploying Looply to Your Organisation via Microsoft Teams

This guide walks your IT administrator through deploying the Looply app to users across your organisation using Microsoft Teams Admin Center and Azure Active Directory (Entra ID).

> **Heads up — bulk rollout takes time** After completing the steps in this guide, the Looply app will not appear for users immediately. Microsoft Teams processes policy assignments in batches across your tenant. Depending on the size of your organisation, this can take anywhere from **a few minutes to 24 hours**. This is expected behaviour controlled by Microsoft — it is not an issue with Looply. Please plan your rollout accordingly.

***

### Before You Begin

Make sure you have the following before starting:

* **Global Administrator** or **Teams Administrator** role in Microsoft 365
* Access to [Azure Portal](https://portal.azure.com) and [Teams Admin Center](https://admin.teams.microsoft.com)
* Looply already consented and configured in your tenant — your Looply account manager will confirm this

> Not sure if Looply is configured for your tenant? Contact your Looply account manager before proceeding. Deploying the app before tenant setup is complete will result in users seeing an error when they open it.

***

### Choosing Your Rollout Approach

Before creating groups or policies, decide how broadly you want to deploy Looply. There are three common approaches:

#### Organisation-wide

Every user in the organisation gets Looply deployed. This suits companies where Looply is a core tool used across all departments — for example, a business that has automated company-wide processes such as onboarding, IT requests, or expense approvals.

* Create a single security group with a dynamic rule like `user.accountEnabled -eq true` to capture all active users
* Or assign the policy directly to all users if your tenant is small
* Any new hire added to Azure AD will automatically receive Looply without any admin action

#### Team or department

Looply is deployed to specific teams — for example, Finance, Purchasing, HR, or Operations. This is the most common approach for organisations that are rolling out Looply incrementally or where only certain departments use automated workflows.

* Create one security group per team: `Looply - Finance Team`, `Looply - Purchasing Team`, `Looply - HR Team`
* Use dynamic membership rules based on the `department` attribute so the groups stay in sync automatically as staff join or move teams
* All groups are assigned to the same `Looply App Policy` — you do not need a separate policy per team
* Example: The Finance team uses Looply for invoice approval workflows. Only Finance staff need the app, so only the `Looply - Finance Team` group is created and assigned

#### Process-specific

Looply is deployed only to users involved in a specific business process, regardless of their department. This suits scenarios where a workflow spans multiple teams but only involves a subset of users.

* Example: An invoice approval process involves the Purchasing team, Finance approvers, and a small group of senior managers. Rather than deploying to entire departments, you create a single group — `Looply - Invoice Approvers` — and add only the relevant users
* Membership is typically managed manually (Assigned type) since the group is curated rather than department-driven
* This approach gives the tightest control over who has access

> **Tip — You can combine approaches** Many organisations start with a team-based rollout (e.g. Finance first) and expand over time. Each team gets its own security group, all assigned to the same Teams Setup Policy. Process-specific groups can be added later for cross-departmental workflows without disrupting the existing setup.

***

### Step 1 — Create a Security Group in Azure Portal

You will use a security group to control which users receive the Looply app.

1. Go to [portal.azure.com](https://portal.azure.com) and sign in with your admin account
2. Navigate to **Azure Active Directory** (Microsoft Entra ID) → **Groups**
3. Click **+ New group**

Fill in the form as follows:

| Field             | Value                                                           |
| ----------------- | --------------------------------------------------------------- |
| Group type        | **Security**                                                    |
| Group name        | e.g. `Looply - Finance Team` or `Looply - Purchasing Team`      |
| Group description | e.g. `Finance team users with Looply deployed via Teams policy` |
| Membership type   | See below                                                       |

#### Membership Type — Which Should You Choose?

**Assigned** — You manually add and remove members. Best for smaller teams or process-specific groups where membership doesn't change often (e.g. a fixed group of invoice approvers).

**Dynamic User** — Members are automatically added based on user attributes in Azure AD. Best for department-level rollouts where you want the group to stay in sync with your directory automatically.

> **Tip — Dynamic membership is ideal for team-based rollouts** If your Azure AD user profiles include department attributes, you can write a rule that automatically keeps the group in sync. For example:
>
> * Finance team: `user.department -eq "Finance"`
> * Purchasing team: `user.department -eq "Purchasing"`
> * HR team: `user.department -eq "Human Resources"`
> * All UK employees: `user.country -eq "GB"`
> * All permanent staff: `user.employeeType -eq "Employee"`
>
> This means when a new Finance hire is added to Azure AD with the correct department, they automatically get Looply deployed — no manual steps required.

4. Click **Create**

***

### Step 2 — Add Members to the Group

#### If you chose Assigned membership

1. Open the group you created
2. Go to **Members** → **+ Add members**
3. Search for users by name or email and select them
4. Click **Select** to confirm

> **Tip** — You can add existing security groups (e.g. an existing `Finance Department` group) as members rather than adding individual users. This avoids duplicating membership management across multiple places.

#### If you chose Dynamic User membership

1. Open the group and go to **Dynamic membership rules**
2. Click **Edit** and use the rule builder or write a rule directly
3. Click **Validate rules** to test against specific users before saving
4. Click **Save**

> Dynamic group population is not instant. After saving a rule, Azure AD may take up to 24 hours to fully evaluate all users in large tenants. Check **Membership processing status** on the group overview page to monitor progress.

***

### Step 3 — Create a Teams App Setup Policy

A Setup Policy tells Microsoft Teams to automatically install Looply for users assigned to that policy. You only need **one policy** regardless of how many groups or rollout approaches you are using.

1. Go to [admin.teams.microsoft.com](https://admin.teams.microsoft.com)
2. Navigate to **Teams apps → Setup policies**
3. Click **+ Add**

> **Do not modify the Global (Org-wide default) policy.** Create a dedicated policy for Looply. This keeps your rollout isolated and easy to reverse if needed.

4. Name the policy — e.g. `Looply App Policy`
5. Under **Installed apps**, click **+ Add apps**
6. Search for **Looply**, click **Add**, then **Add** again to confirm
7. Click **Save**

***

### Step 4 — Assign the Policy to Your Group

The Setup policies page has two tabs — **Manage policies** (where you created the policy in Step 3) and **Group policy assignment** (where you assign it to your group). Make sure you are on the correct tab.

Each group assignment is done one at a time. If you have multiple groups (e.g. Finance, Purchasing, HR), repeat this process for each group — each one is assigned to the same `Looply App Policy`.

1. In Teams Admin Center, go to **Teams apps → Setup policies**
2. Click the **Group policy assignment** tab
3. Click **+ Add group**
4. Search for and select your security group (e.g. `Looply - Finance Team`)
5. Under **Select a policy**, choose `Looply App Policy`
6. Set the **Rank** to `1` unless you have other conflicting policies
7. Click **Apply**
8. Repeat steps 3–7 for each additional group

> **Note — Rollout approach examples**
>
> * **Organisation-wide**: Assign your single all-staff group (e.g. `Looply - All Staff`) once
> * **Team or department**: Repeat the assignment for each team group — `Looply - Finance Team`, `Looply - Purchasing Team`, `Looply - HR Team` — each assigned to `Looply App Policy`
> * **Process-specific**: Assign your curated group (e.g. `Looply - Invoice Approvers`) once

***

### Installing for Individual Users

For one-off installations — for example, a new starter, a contractor, or a user who needs access outside of their team group — use the **Looply App Manager** rather than modifying your Azure AD groups or Teams policies.

The Looply App Manager allows your Looply administrator to install and uninstall the app for individual users directly from within Looply, without requiring IT involvement.

To access it, sign in to Looply and navigate to **App Manager** from the main menu. From there you can search for users and install or uninstall the app individually.

> **When to use Looply App Manager vs Teams Admin deployment**

| Scenario                                | Use                                                        |
| --------------------------------------- | ---------------------------------------------------------- |
| Deploying to a whole team or department | Teams Admin Center + Azure AD group                        |
| New starter joining an existing group   | Add them to the Azure AD group — app deploys automatically |
| One-off user outside of a group         | Looply App Manager                                         |
| Contractor or temporary user            | Looply App Manager                                         |
| Removing a specific user's access       | Looply App Manager or remove from Azure AD group           |

***

### Step 5 — Verify the Rollout

Once the policy is assigned, Teams will begin deploying Looply to users in the group.

**To check policy assignment:**

1. In Teams Admin Center, go to **Teams apps → Setup policies**
2. Click the **Group policy assignment** tab — your group should be listed with `Looply App Policy` shown against it

**To check a specific user:**

1. Go to **Users → Manage users**
2. Find the user and open their profile
3. Check the **Policies** tab — App setup policy should show `Looply App Policy`

**To verify from the user's side:** Ask a test user to open Microsoft Teams — Looply should appear in their left sidebar or app bar once the rollout has reached them.

> **Tip — Track policy assignments via the Activity log** You can audit when a policy was assigned to a group and which admin made the change. In Teams Admin Center, go to **Users → Activity log** to see a history of policy assignment operations, including timestamps and the admin account that performed each action. This is useful for confirming the rollout was triggered and when to expect it to complete.

> **Still not seeing the app?** If users don't see Looply after completing these steps, wait up to 24 hours before investigating. Microsoft Teams processes policy rollouts gradually — this is not an issue with Looply. If the app has still not appeared after 24 hours, contact your Looply account manager with your tenant ID and the name of the policy and group you created.

***

### Important — Looply Is Notified When the User First Opens Teams

When an admin deploys Looply via a Setup Policy, the app is provisioned to the user's account in the Microsoft 365 backend. However, **Looply is only notified of the installation when the user's Teams client processes it** — which happens the first time the user opens Microsoft Teams after the policy has been applied.

This means:

* If a user has not yet logged into Teams (e.g. a new starter who hasn't set up their device yet), Looply will not show them as installed until they do
* This is expected behaviour defined by Microsoft — the installation event is fired by the Teams client, not Microsoft's backend infrastructure
* There is no action required from the administrator — Looply will be notified automatically once the user opens Teams

> **Note for administrators** If you need to confirm that Looply has been successfully set up for a user before they have logged into Teams for the first time, contact your Looply account manager. Do not assume a missing user record in Looply indicates a failed installation — it may simply mean the user hasn't opened Teams yet.

***

### Removing the App from Users

| Action                 | How                                                               |
| ---------------------- | ----------------------------------------------------------------- |
| Remove a specific user | Remove them from the Azure AD group, or use Looply App Manager    |
| Remove an entire team  | Remove the group from the policy assignment in Teams Admin Center |
| Remove Looply org-wide | Delete the `Looply App Policy` entirely                           |

> Removing a user from the group or policy does **not** delete their Looply data. Their workflows and history remain intact and can be reassigned or archived by a Looply administrator.


# Uninstall/Update Looply Apps

Looply apps can be uninstalled or updated across organisation from the **User Manager** tab.&#x20;

* Go to **App Manager** from the side navigation menu
* Click on the App tile to manage uninstallation/update
* Go to **User Manager** tab
* Search for the user using their email address
* Click Uninstall/Force update&#x20;

<figure><img src="/files/VrdgJcuu1KdBsCp68WJ4" alt=""><figcaption></figcaption></figure>


# Teams Admin center

Guide to Pre-Install an Looply App via Teams Admin Center to organizational users

## **Prerequisites**

**Admin Access:** You must have admin permissions to access the Microsoft Teams Admin Center.

**Looply App:** Ensure the Looply app is already listed under Teams apps > Manage apps. ([Refer to deploying apps to App catalog section](/app-management/deploying-apps-to-teams-app-catalog))

## **Steps to Pre-Install the App**

1. **Log in to the Teams Admin Center**
   * Visit the Teams Admin Center (<https://admin.teams.microsoft.com>).
   * Sign in using your Microsoft 365 admin credentials.

<figure><img src="/files/u2KgJ64QGE0uBYYP1ojN" alt=""><figcaption></figcaption></figure>

1. **Navigate to App Setup Policies**
   * In the left navigation pane, go to **Teams apps > Setup policies.**
   * Review the available setup policies or create a new one.
2. **Add the App to a Setup Policy**
   * Choose an existing policy to modify or click Add to create a new one.
   * Under the Installed apps section:
     * Click Add apps.
     * Search for the Looply app by name (eg: Task order).
     * Select the app and click Add.
   * (Optional) Under the Pinned apps section:
     * Click Add apps.
     * Search for the app by name and click Add to make it appear in the Teams navigation bar.
     * Adjust the order of pinned apps using the Move up or Move down buttons.
3. **Assign the Policy to Users**
   * Go to Users > Manage users.
   * Search for the user or group of users to whom you want to assign the policy.
   * Click the user’s name, and under Policies, click Edit.
   * Assign the desired app setup policy.&#x20;
   * Bulk Assignment:&#x20;
     * To assign the policy to multiple users, go to Teams apps > Setup policies, select the policy, and click Assign users.&#x20;
     * Upload a CSV file containing the list of user email addresses or manually select users.
   * (Alternatively) go to **Teams apps > Setup policies >** Manager users > Assign to required teams users&#x20;

<figure><img src="/files/IjRrpfZP1dZBK4Kyu6uq" alt=""><figcaption></figcaption></figure>

1. **Verify Deployment**
   1. Ask a test user to log into Teams and verify that the app is pre-installed and, if pinned, visible in  the navigation bar.
   2. &#x20;You can check the app's status in Teams apps > Manage apps or through user activity logs.

***

## FAQs

1\) How long does it take for the app to appear?

It may take a few hours for the policy changes to propagate to all assigned users.

2\) What if the app is not visible?

·       Ensure the app is published in the Manage apps section.

·       Verify that the setup policy is correctly assigned to the user or group.

·       Get users to signout of teams and sign back in

3\) Can I enforce app usage?

Pre-installation does not enforce usage, but pinning the app ensures high visibility.


# Installation Health (Beta)

The Installation Health tab helps administrators verify whether installed Teams apps can actually deliver messages to users. An app may appear "installed" in Microsoft but still fail to deliver messages for several reasons — the user hasn't opened Teams, the conversation has gone stale, or the user has blocked the bot. Installation Health performs deep checks to uncover these issues.

{% hint style="info" %}
**Note:** Installation Health is currently in Beta. Access it from **App Manager → select your app →**&#x20;

**Installation Health** tab.
{% endhint %}

<figure><img src="/files/yyCtjxvEH3JxrhO1T0Qj" alt=""><figcaption></figcaption></figure>

### Health Statuses

After running a health check, each user displays one of the following statuses:

| Status            | Colour | What it means                                                                 | What to do                                            |
| ----------------- | ------ | ----------------------------------------------------------------------------- | ----------------------------------------------------- |
| **Healthy**       | Green  | Everything is working. Messages will be delivered.                            | No action needed.                                     |
| **Recovered**     | Green  | A connection issue was detected and automatically fixed.                      | No action needed. Messages can now be delivered.      |
| **Provisioning**  | Amber  | The app is installed but the user hasn't opened Teams yet.                    | Ask the user to open Teams and wait a few minutes.    |
| **No Context**    | Red    | Looply is missing the connection details needed to send messages.             | Uninstall and reinstall the app for this user.        |
| **Stale Context** | Red    | The connection details are outdated and could not be automatically recovered. | Uninstall and reinstall the app for this user.        |
| **Blocked**       | Red    | The user has blocked the bot in Teams.                                        | The user must unblock it — see troubleshooting below. |
| **Not Installed** | Red    | The app is no longer installed for this user.                                 | Reinstall the app.                                    |
| **Failed**        | Red    | The health check encountered an error.                                        | Try running the check again.                          |
| **Not Checked**   | Grey   | No health check has been run yet.                                             | Click the health check button to check this user.     |

### User Status

The User Status column shows the user's current Teams presence — Available, Busy, Away, Offline, etc. This is informational only and does not affect the health status. It can help determine whether the user is actively using Teams.

### Actions

Each user row provides the following actions:

#### Profile Sync

Fetches the latest user profile from Microsoft and updates the local record — including display name, email, and job title.

**When to use:**

* A user's name or email has changed but Looply still shows the old details
* The email column appears blank or outdated
* After organisational changes such as name changes or email migrations

{% hint style="info" %}
**Tip:** Profile data is not automatically refreshed. Click the sync button to pull the latest details. The table updates immediately after a successful sync.
{% endhint %}

***

#### Check Health

Runs a comprehensive check to verify whether the app installation is fully functional and messages can be delivered.

**When to use:**

* After installing the app for a user, to confirm it completed successfully
* When a user reports they are not receiving messages
* After a user re-installs the app or changes devices
* To periodically audit installation health across your organisation

{% hint style="info" %}
**Tip:** If the status returns **Provisioning**, ask the user to open Microsoft Teams and wait a few minutes before checking again. If the status is **No Context** or **Stale Context**, uninstall and reinstall the app for that user.
{% endhint %}

***

#### Send Test Message

Sends a test card to the user's Teams chat to verify end-to-end message delivery.

**When to use:**

* After a health check returns **Healthy**, to confirm actual delivery
* When a user reports missing notifications despite a healthy status
* After resolving a blocked bot or completing a reinstallation

**Limits:**

* Only one test message can exist per user at a time
* If delivery fails, a 60-second cooldown applies before retrying
* The error notification will indicate the specific reason for failure

Once a test message is successfully sent, a **delete button** appears next to the send button. Use it to remove the test message from the user's chat after confirming delivery. The delete button remains available across page refreshes until the message is deleted.

{% hint style="info" %}
**Tip:** Delete the test message after confirming delivery to avoid confusing end users.
{% endhint %}

### Searching

Use the search field at the top of the tab to find users by name or email. The search covers all users in your organisation, not just those on the current page.

### Last Health Check

This column shows when the most recent action was performed, displayed as relative time (e.g., "2m ago", "3h ago", "Yesterday"). Hover to see the exact timestamp. The type of action is shown below — such as "Health check", "Test message", "Profile sync", or "Delete message".

### Troubleshooting&#x20;

| Symptom                             | Likely Status                      | Resolution                                                                                                                 |
| ----------------------------------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| User not receiving messages         | **Provisioning**                   | Ask the user to open Microsoft Teams and wait a few minutes                                                                |
| User not receiving messages         | **No Context** / **Stale Context** | Uninstall and reinstall the app for this user                                                                              |
| User not receiving messages         | **Blocked**                        | User must unblock the bot: **Teams → Chat → find the bot conversation → right-click → Unblock**                            |
| User not receiving messages         | **Not Installed**                  | Reinstall the app from the User Manager tab                                                                                |
| User not receiving messages         | **Healthy**                        | Send a **Test Message** to confirm delivery — if it fails, check the error for the specific reason                         |
| Name or email is incorrect          | —                                  | Click **Profile Sync** to fetch the latest details from Microsoft                                                          |
| App reinstalled but still unhealthy | **No Context**                     | Wait 1–2 minutes, run a health check again. If still unhealthy, ask the user to open Teams and send any message to the bot |
| Test message not arriving           | Any non-Healthy                    | Resolve the health status first, then retry the test message                                                               |
| Health check returns **Failed**     | **Failed**                         | This is usually a temporary issue. Wait a moment and try again                                                             |


# Bulk Install (Beta)

The Bulk Install tab allows administrators to install the Looply app for multiple users at once. This is ideal for large-scale rollouts across teams, departments, or entire organisations.

{% hint style="info" %}
**Note:** Access Bulk Install from **App Manager → select your app → Bulk Install** tab.
{% endhint %}

{% hint style="warning" %}
**Prerequisite:** The app must be deployed to your organisation's Teams app catalog before bulk installation can proceed.
{% endhint %}

### Overview

Bulk Install has two views:

* **Batch Jobs** — A list of all previous and active installation jobs
* **New Batch** — A user selection screen where you build and submit a new batch

### Creating a New Batch

<details>

<summary>Step 1: Click "New Batch"</summary>

From the Batch Jobs view, click **New Batch** in the top right corner. This opens a split-screen view with a user browser on the left and your selected batch on the right.

</details>

<details>

<summary>Step 2: Select Users</summary>

The left panel displays all users in your Microsoft organisation with their **Display Name** and **User Principal Name** (email).

* Click the **checkbox** next to individual users to add them to the batch
* Use the **Select All** checkbox in the header row to select all eligible users on the current page
* Use the **search bar** to find users by name or email
* Navigate through pages to find users across your organisation

Users who already have the app installed are shown with an **Installed** badge and cannot be selected.

</details>

<details>

<summary>Step 3: Name the Batch (Optional)</summary>

Enter a name in the **Batch Name** field on the right panel. This helps identify the job later. If left blank, it appears as "Bulk Install Batch".

</details>

<details>

<summary>Step 4: Review and Submit</summary>

The right panel shows all selected users. You can:

* Review the full list before submitting
* Remove individual users by clicking the remove button next to their name
* Click **Submit** to start the installation

</details>

{% hint style="warning" %}
**Limit:** Each batch supports a maximum of **500 users**. The selection counter shows how many users are selected.
{% endhint %}

{% hint style="info" %}
**Note:** Once submitted, the batch processes in the background. You can safely navigate away — the installation will continue and you can check progress by returning to the Bulk Install tab.
{% endhint %}

### Monitoring Batch Jobs

After submitting, you are returned to the **Batch Jobs** view. Each job displays:

| Column          | Description                                    |
| --------------- | ---------------------------------------------- |
| **Batch Name**  | The name you gave the batch                    |
| **App Version** | The version of the app being installed         |
| **Status**      | Current state — see below                      |
| **Progress**    | Completed and failed installs out of the total |
| **Created**     | When the batch was submitted                   |
| **Completed**   | When the batch finished processing             |

#### Job Statuses

{% hint style="success" %}
**DONE** — All users in the batch were processed successfully.
{% endhint %}

{% hint style="warning" %}
**PARTIAL** — The batch completed but some users failed. Click the job to see details.
{% endhint %}

{% hint style="info" %}
**PROCESSING** — The batch is actively installing. The progress bar updates in real time and the list auto-refreshes every 15 seconds.
{% endhint %}

### Viewing Job Details

Click any job row to open the **Job Detail** dialog showing every user in the batch with their individual status:

{% hint style="success" %}
**Installed** — App was successfully installed. Hover for the installation timestamp.
{% endhint %}

{% hint style="info" %}
**Pending** — Still waiting to be processed.
{% endhint %}

{% hint style="warning" %}
**Failed** — Shown with a specific reason. See failure reasons below.
{% endhint %}

If there were infrastructure-level errors, they appear in a warning banner at the top of the dialog.

#### Failure Reasons

| Reason                           | Meaning                                                            |
| -------------------------------- | ------------------------------------------------------------------ |
| **Already installed**            | The app was already installed for this user                        |
| **Insufficient permissions**     | Teams admin policies do not allow installing the app for this user |
| **User not found**               | The user could not be found or resolved in Microsoft Graph         |
| **Microsoft Graph server error** | A temporary error on Microsoft's side — retried automatically      |
| **Max retries exceeded**         | Installation was retried multiple times but continued to fail      |
| **Failed to queue**              | The installation request could not be queued for processing        |

### Troubleshooting

<details>

<summary>Batch taking a long time to complete</summary>

Large batches may take several minutes due to Microsoft Graph rate limiting. The system handles retries automatically. The progress bar will continue updating — wait for the batch to complete.

</details>

<details>

<summary>Many users showing "Insufficient permissions"</summary>

Your Teams admin policies may be restricting app installation for these users. Check **Teams Admin Center → Teams apps → Permission policies** to ensure the app is allowed for the affected users or groups.

</details>

<details>

<summary>Users showing "Already installed"</summary>

No action needed — the app was previously installed for these users, either individually from the User Manager tab or in a prior batch.

</details>

<details>

<summary>Users not appearing in the user list</summary>

The user may not have an active Microsoft Teams licence. Verify in **Azure AD → Users → select the user → Licences** that a Teams-enabled licence is assigned.

</details>

<details>

<summary>Batch completed but users aren't receiving messages</summary>

The app is installed but the conversation may not be fully provisioned yet. Use the **Installation Health** tab to run health checks on affected users and identify the specific issue.

</details>

<details>

<summary>Checkboxes are disabled for some users</summary>

Either the user already has the app installed (shown with an **Installed** badge), or you have reached the **500 user limit** for a single batch. Submit the current batch first, then create a new one for the remaining users.

</details>


# Building Adaptive Cards

Adaptive Cards are a platform-agnostic and customisable way to create interactive and data driven messages that are actioned upon using Microsoft Teams.

Start by clicking on the **Template Designer** and then clicking on **New Card** within the **Start menu.** Enter a name for your card and then click the **Create** Button.

## The Adaptive Card Designer

The Adaptive Card Designer, created by Microsoft, has been imported into Looply to create Adaptive Cards for Looply Workflows. It provides an easy way to create beautiful data-rich messages that can be sent to teams. This designer provides massive benefits when combined with Looply as data fetched and generated via Looply Workflows can be bound and displayed to your end users in a dynamic way. The Adaptive Card Designer is here to provide you with the tools to create a template for your Adaptive Card so that in each process a dynamically data-driven card can be created.

[**Check out our in-depth tutorial on Building Adaptive Cards**](/tutorials/building-adaptive-cards)[**!**](/tutorials/building-adaptive-cards)

There are 8 main areas of the Adaptive Card Designer:

### Card Toolbar

This toolbar is located at the top of the Adaptive Card Designer. This acts as a file menu where you can create new cards, undo and redo actions, deploy the card, and alter the several views of the Adaptive Card Designer. You also have quick access to an AI Assistant, and the ability to test your card to see how it would look on Microsoft Teams.

### Card Elements Toolbar

Located to the left of your screen are all the available elements ready to use to create your card. Below are links to understand each element:

* [Container Elements](/adaptive-cards/building-adaptive-cards/container-elements)
* [Content Elements](/adaptive-cards/building-adaptive-cards/content-elements)
* [Input Elements](/adaptive-cards/building-adaptive-cards/input-elements)

### Adaptive Card Canvas

Located in the centre (just right of the [Card Elements Toolbar](#card-elements-toolbar)) of the Adaptive Card Designer is the canvas. Drag and drop elements from the [Card Elements Toolbar](#card-elements-toolbar) to create your card. Drag elements already placed on the canvas to realign them. Certain elements placed on the canvas have buttons to add [actions](/adaptive-cards/building-adaptive-cards/actions) or more [columns](/adaptive-cards/building-adaptive-cards/container-elements#columnset) if the respective element has been selected. More on this from the respective element sections from the [Card Elements Toolbar](#card-elements-toolbar) section above.

### Card Structure Toolbar

Just right of the [Adaptive Card Canvas](#adaptive-card-canvas), you'll find the structure of the card you are currently building. Provided as a tree structure, use this to quickly select elements.

### Element Properties Toolbar

On the right of the Adaptive Card Designer is the Element Properties Toolbar. When an element is selected on the Adaptive Card Canvas you will be provided with a menu to style the respective component. This will contain fields to change text, colour, alignment, and potentially add [actions](/adaptive-cards/building-adaptive-cards/actions) if the element is clicked. Read more on this by finding out about how each element can be altered via the [Card Elements Toolbar](#card-elements-toolbar) section above.

### Card Payload Editor

The first of the 2 text editors. This contains the JSON of the Adaptive Card that you will build. You can edit the text in this editor to add your own [bindings](/adaptive-cards/data-binding) or paste in an Adaptive Card you have already made elsewhere.&#x20;

{% hint style="warning" %}
Note: Manually typing in the Card Payload Editor may cause the card to disappear from the Adaptive Card Canvas. This occurs when the JSON supplied is incorrect.
{% endhint %}

### Sample Data Editor

The second of the 2 editors. This editor contains a sample data schema, which can either be hand-typed or dynamically passed in if the card was created alongside a workflow. Providing data to this text editor will provide data for the [data binding process](/adaptive-cards/data-binding).

### Footer Toolbar

Located at the bottom of the Adaptive Card Designer. This will alert you to the Adaptive Card's name, if it has a Looply Workflow attached, and if the Card has been [activated](#card-versioning).

## Card Versioning

Currently, Adaptive Cards created are being versioned. Each card is initially given **Draft** status. Once the card has been **Activated** that will lock this version in place.&#x20;

{% hint style="info" %}
Note: Looply Workflows using Adaptive Cards will always use the latest **Active** Adaptive Card version.
{% endhint %}

Saving a change made to an Active Adaptive Card will trigger a new version to be made. The new version of the card that's made is automatically placed back into **Draft** status.&#x20;

{% hint style="warning" %}
Note: You can still use **Draft** Adaptive Cards within your Workflows, however, if accidental changes are made to the Card subsequent messages containing this card will be affected.
{% endhint %}

## Shortcuts

To make designing Adaptive Cards easier we created some shortcuts for frequent actions.

| Shortcut(Windows) | Shortcut(Mac) | Action       |
| ----------------- | ------------- | ------------ |
| CTRL+SHIFT+E      | CMD+SHIFT+E   | New Card     |
| CTRL+S            | CMD+S         | Save Card    |
| CTRL+SHIFT+R      | CMD+SHIFT+R   | Rename Card  |
| CTRL+Z            | CMD+Z         | Undo         |
| CTRL+SHIFT+Z      | CMD+SHIFT+Z   | Redo         |
| CTRL+SHIFT+L      | CMD+SHIFT+L   | AI Assistant |


# Container Elements

Adaptive Card elements used to configure and organise an Adaptive Cards layout.

## Container

#### Description

This is the standard container that can hold any and all other elements. This container is very useful if you have a single column of elements that you may need to render. Great for background images/colours, and alignment of child elements - all controllable from the [**Element Properties Toolbar**](/adaptive-cards/building-adaptive-cards#element-properties-toolbar).&#x20;

#### Technical Documentation

[**Read more about the Container**](https://adaptivecards.io/explorer/Container.html)

## ImageSet

#### Description

Used for adding a collection of images. Each image takes its own column - acts as a carousel of images. You must use an HTTP URL for each of the images. You can add new images by selecting the ImageSet from the [**Adaptive Card Canvas**](/adaptive-cards/building-adaptive-cards#adaptive-card-canvas) and clicking on the image icon on the bottom right of the selected element.

#### Technical Documentation

[**Read more about the ImageSet**](https://adaptivecards.io/explorer/ImageSet.html)

## FactSet

#### Description

Used as a 2-column table of information. Mimics a key-value table. Each new key-value pairing is a new row in the FactSet. This only allows text in both key and value text blocks. Add new fact sets from the [**Element Properties Toolbar**](/adaptive-cards/building-adaptive-cards#element-properties-toolbar)**.**

#### Technical Documentation

[**Read more about the FactSet**](https://adaptivecards.io/explorer/FactSet.html)

## ColumnSet

#### Description

This container will allow you to add multiple columns. Gives the opportunity to add more elements or containers side by side. You can add a new column by clicking on the ColumnSet that has been added to the [**Adaptive Card Canvas**](/adaptive-cards/building-adaptive-cards#adaptive-card-canvas) and selecting the icon at the bottom right. You can add any element to a column of a ColumnSet.

Each column can also have a weight set against it by changing the **Width** parameter in the [**Element Properties Toolbar**](/adaptive-cards/building-adaptive-cards#element-properties-toolbar) to Weighted from Stretched and assigning a weight to the Weight field, this will be a percentage value.&#x20;

Columns share the same styling, and alignment features as the standard [**Container**](#container) element.

#### Technical Documentation

[**Read more about the ColumnSet**](https://adaptivecards.io/explorer/ColumnSet.html)

## Table

#### Description

Aligns elements in a table format.&#x20;

You can add rows by selecting the **Table** on the [**Adaptive Card Canvas**](/adaptive-cards/building-adaptive-cards#adaptive-card-canvas) and then clicking the "**Add a row**" button. Remove a row by selecting the TableRow from the [**Card Structure Toolbar**](/adaptive-cards/building-adaptive-cards#card-structure-toolbar) and then click either the "**X**" on the selected row or by pressing "**Backspace.**"

Adding and removing columns for a row is easy as well, select the **Table**, navigate to the [**Element Properties Toolbar**](/adaptive-cards/building-adaptive-cards#element-properties-toolbar), scroll to the bottom, and add/remove columns.&#x20;

Each column can have its own weight - this is a proportional size against the other columns. e.g. Column A could be size 2 and Column B has a size of 1, this means Column A is 2x bigger than Column B.

Each TableCell, TableRow, and the Table itself can be styled like the base Container Element.

#### Technical Documentation

[**Read more about the Table**](https://adaptivecards.io/explorer/Table.html)


# Content Elements

Add content to your Adaptive Cards in the form of Text, Images, Videos and Buttons

## TextBlock

#### Description

This is the standard TextBlock used to display basic text. This element has simple styling tools to change font weight, alignment, size, and colour as well as the maximum number of lines to display all controllable from the [**Element Properties** **Toolbar**](/adaptive-cards/building-adaptive-cards#element-properties-toolbar).

#### Technical Documentation

[**Read more about the TextBlock**](https://adaptivecards.io/explorer/TextBlock.html)

## RichTextBlock

#### Description

A more advanced version of the TextBlock where you must use the [**Card Payload Editor**](/adaptive-cards/building-adaptive-cards#card-payload-editor) to make changes and enhancements. The RichTextBlock will allow you to segment your content into sections allowing each section to be styled differently.

#### Technical Documentation

[**Read more about the RichTextBlock**](https://adaptivecards.io/explorer/RichTextBlock.html)

## Image

#### Description

Allows the ability to add an Image. Must be a valid HTTP URL pointing to an image. Can be styled from the [**Element Properties Toolbar**](/adaptive-cards/building-adaptive-cards#element-properties-toolbar) and can have an actionable trigger if the image is clicked. [**See the Actions section for more.**](/adaptive-cards/building-adaptive-cards/actions)

#### Technical Documentation

[**Read more about Image**](https://adaptivecards.io/explorer/Image.html)

## Media

#### Description

Attach video/audio files from external sites. You can attach multiple media options to the same **Media** element. Meaning you could have both an audio recording and a video within the same element.&#x20;

If there is a video linked to the Media element then you can add your own thumbnail to overlay over the video, if this is not supplied then the thumbnail from the video is used.

You can style the alignment of the Media element from the [**Element Properties Toolbar**](/adaptive-cards/building-adaptive-cards#element-properties-toolbar).&#x20;

#### Technical Documentation

[**Read more about Media**](https://adaptivecards.io/explorer/Media.html)

## ActionSet

#### Description

A group of buttons aligned along a row. Each button will trigger an Action. [**Learn more about actions from the Action section**](/adaptive-cards/building-adaptive-cards/actions). Adding buttons is easy: select the ActionSet and click **"Add an action"** and select from the dropdown, or go to the [**Element Properties Toolbar**](/adaptive-cards/building-adaptive-cards#element-properties-toolbar) for the Action Set and select **"Add an action"** then select from the dropdown.&#x20;

Style the **ActionSet** from the [**Element Properties Toolbar**](/adaptive-cards/building-adaptive-cards#element-properties-toolbar).

Each **Button** within the **ActionSet** can also be styled, simply click on the **Button** and alter it from the [**Element Properties Toolbar**](/adaptive-cards/building-adaptive-cards#element-properties-toolbar).

#### Technical Documentation

[**Read more about an ActionSet**](https://adaptivecards.io/explorer/ActionSet.html)


# Input Elements

Adaptive Cards that act as Forms to retrieve data from an end user.

## Input.Text

#### Description

A standard input field that will allow users to type in text. This input can be assorted with a Label and Placeholder.&#x20;

Regex validation is supported on this field to validate inputs. If validation fails the error message is then shown.&#x20;

This Input can have an **Inline Action** where the user can submit the data from this input field straight back to Looply for processing. [This can be any of the Actions from the Action section.](/adaptive-cards/building-adaptive-cards/actions)

You can change this input to a multi-line input field where a larger response can be retrieved.&#x20;

Applying an **Id** to this input field will mean the data passed to Looply will be attributed to it. e.g An Id of `first_name` and an input field value of `John` will be passed to Looply as `{"first_name": "John"}.`

All of the above is edited using the [**Element Properties Toolbar**](/adaptive-cards/building-adaptive-cards#element-properties-toolbar) that is found when clicking on the respective Input.Text field.

#### Technical Documentation

[**Read more about Input.Text**](https://adaptivecards.io/explorer/Input.Text.html)

## Input.Date

#### Description

A field designed for taking in dates from the end user.&#x20;

Provides a calendar for the end user to select a date.&#x20;

This input can be assorted with a Label and a Default value.&#x20;

You can supply a **Min** and a **Max** value for validation where if the user incorrectly adds a date will show the error message that is specified in the respective [**Element Properties Toolbar**](/adaptive-cards/building-adaptive-cards#element-properties-toolbar)**.**&#x20;

All Dates added must follow the `YYYY-MM-DD` standard.

Applying an **Id** to this input field will mean the data passed to Looply will be attributed to it. e.g An Id of `date` and an input field value of `2023-10-17` will be passed to Looply as `{"date": "2023-10-17"}.`

All of the above is edited using the [**Element Properties Toolbar**](/adaptive-cards/building-adaptive-cards#element-properties-toolbar) toolbar that is found when clicking on the respective Input.Date field.

#### Technical Documentation

[**Read more about Input.Date**](https://adaptivecards.io/explorer/Input.Date.html)

## Input.Time

#### Description

A field designed for taking in times from the end user. &#x20;

This input can be assorted with a Label and a Default value.&#x20;

You can supply a **Min** and a **Max** value for validation where if the user incorrectly adds a time will show the error message that is specified in the respective [**Element Properties Toolbar**](/adaptive-cards/building-adaptive-cards#element-properties-toolbar).&#x20;

All Times added must follow the `MM:HH` standard.

Applying an **Id** to this input field will mean the data passed to Looply will be attributed to it. e.g An Id of `time` and an input field value of `15:30` will be passed to Looply as `{"time": "15:30"}.`

All of the above is edited using the [**Element Properties Toolbar**](/adaptive-cards/building-adaptive-cards#element-properties-toolbar) that is found when clicking on the respective Input.Time field.

#### Technical Documentation

[**Read more about Input.Time**](https://adaptivecards.io/explorer/Input.Time.html)

## Input.Number

#### Description

A field designed for taking in numbers from the end user. &#x20;

This input can be assorted with a Label, placeholder, and a Default value.&#x20;

You can supply a **Min** and a **Max** value for validation where if the user incorrectly adds a number will show the error message that is specified in the respective [**Element Properties Toolbar**](/adaptive-cards/building-adaptive-cards#element-properties-toolbar).&#x20;

Applying an **Id** to this input field will mean the data passed to Looply will be attributed to it. e.g An Id of `price` and an input field value of `1.10` will be passed to Looply as `{"price": "1.10"}.`

All of the above is edited using the [**Element Properties Toolbar**](/adaptive-cards/building-adaptive-cards#element-properties-toolbar) that is found when clicking on the respective Input.Number field.

#### Technical Documentation

[**Read more about Input.Number**](https://adaptivecards.io/explorer/Input.Number.html)

## Input.ChoiceSet

#### Description

A field designed to replicate a dropdown menu.&#x20;

This input can be assorted with a Label, placeholder, and a Default value.&#x20;

If a user incorrectly selects or types a value not within the ChoiceSet scope of values then an error is shown. That error message is customised via the [**Element Properties Toolbar**](/adaptive-cards/building-adaptive-cards#element-properties-toolbar).

There are a few different types of ChoiceSet: **Compact**, **Expanded**, and **Filtered.** This can be changed from the [**Element Properties Toolbar**](/adaptive-cards/building-adaptive-cards#element-properties-toolbar) using the Style property.

* Compact
  * Follows the same style as a regular dropdown menu.
* Expanded
  * Changes the **ChoiceSet** into **radio buttons** or **checkboxes** depending on if **multi-selection** is selected.
* Filtered
  * Changes the **ChoiceSet** to appear as an Input.Text field but will autocomplete values if the user starts to type a value from the **ChoiceSet** array.

Applying the Multi-Selection option to the **ChoiceSet** will always change the **ChoiceSet** into a group of checkboxes where each checkbox points to a choice.

Applying an **Id** to this input field will mean the data passed to Looply will be attributed to it. e.g An Id of `choice` and an input field value of `1` will be passed to Looply as `{"choice": "1"}.`

All of the above is edited using the [**Element Properties Toolbar**](/adaptive-cards/building-adaptive-cards#element-properties-toolbar) that is found when clicking on the respective Input.ChoiceSet field.

#### Technical Documentation

[**Read more about Input.ChoiceSet**](https://adaptivecards.io/explorer/Input.ChoiceSet.html)

## Input.Toggle

#### Description

Input to replicate a checkbox.&#x20;

This input can be assorted with a Label, a Title, and a Default value

Labels will go above the checkbox whilst Titles will go to the side.&#x20;

Specific values can be assigned when the Toggle has been toggled "On" or "Off"

Applying an **Id** to this input field will mean the data passed to Looply will be attributed to it. e.g An Id of `toggle` and an input field value of `true` will be passed to Looply as `{"toggle": "true"}.`

All of the above is edited using the [**Element Properties Toolbar**](/adaptive-cards/building-adaptive-cards#element-properties-toolbar) that is found when clicking on the respective Input.Toggle field.

#### Technical Documentation

[**Read more about Input.Toggle**](https://adaptivecards.io/explorer/Input.Toggle.html)


# Actions

Adaptive Cards have a wide range of actions that can be triggered in a few different ways.

## Trigger Points

There are a few ways to trigger the following actions. Options range from a standard button from inside an [**ActionSet**](/adaptive-cards/building-adaptive-cards/content-elements#actionset) or an [**Image**](/adaptive-cards/building-adaptive-cards/content-elements#image) **:**&#x20;

* Adding the **ActionSet** to your Adaptive Card and selecting "Add an action" from the [**Card Canvas.**](/adaptive-cards/building-adaptive-cards#adaptive-card-canvas)
* Adding an [**Image**](/adaptive-cards/building-adaptive-cards/content-elements#image) and selecting an **Action Type** from the dropdown menu within the [**Element Properties Toolbar**](/adaptive-cards/building-adaptive-cards#element-properties-toolbar)**.**
* Selecting a column from a [**ColumnSet**](/adaptive-cards/building-adaptive-cards/container-elements#columnset) and selecting an **Action Type** from the dropdown menu within the [**Element Properties Toolbar**](/adaptive-cards/building-adaptive-cards#element-properties-toolbar)**.**
* Selecting a TableCell from a [**Table**](/adaptive-cards/building-adaptive-cards/container-elements#table) and selecting an **Action Type** from the dropdown menu within the [**Element Properties Toolbar**](/adaptive-cards/building-adaptive-cards#element-properties-toolbar)**.**
* Add actions directly onto the card by selecting the [**Card Canvas**](/adaptive-cards/building-adaptive-cards#adaptive-card-canvas) and clicking "Add an action".

## Action.OpenUrl

#### Description

A simple action that will open whatever URL you put in the "Url" parameter in the [**Element Properties Toolbar**](/adaptive-cards/building-adaptive-cards#element-properties-toolbar)**.**

#### **Technical Documentation**

[**Read more about Action.OpenUrl**](https://adaptivecards.io/explorer/Action.OpenUrl.html)

## Action.Submit

#### Description

Acts as the main action button for sending data back to Looply.&#x20;

Use this action for "Approving" or "Rejecting" cards or if your card contains Input Elements where the data you want to use will be sent to your Looply Workflow. When a user clicks on this action any data bound to the action is sent back to Looply as well.&#x20;

For example, a card that is of this shape:

```json
{
    "type": "AdaptiveCard",
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "version": "1.5",
    "body": [
        {
            "id": "input1",
            "type": "Input.Text",
            "placeholder": "Placeholder text"
        },
        {
            "id": "input2",
            "type": "Input.Text",
            "placeholder": "Placeholder text"
        }
    ],
    "actions": [
        {
            "style": "positive",
            "data": {
                // attach your own data here.
                "action": "APPROVE"
            },
            "title": "Approve",
            "type": "Action.Submit"
        }
    ]
}
```

The above card has 2 [**Input.Text**](https://academy.looply.ai/adaptive-cards/building-adaptive-cards/pages/NyuKVutiQ3m36qS4Iw7Q#input.text) fields and an **Action.Submit** action. If both fields are filled and the button is clicked then the data sent to Looply will look like this:

```json
{
    "data": {
        "action": "APPROVE",
        "input1": "hello",
        "input2": "world"
    }
}
```

{% hint style="info" %}
Note: Looply will know which workflow this data will be sent to when a user clicks on the submit button, therefore, you may see additional information being transferred back to the Looply Workflow.
{% endhint %}

#### Technical Documentation

[**Read more about Action.Submit**](https://adaptivecards.io/explorer/Action.Submit.html)

#### [**Check out Chapter 5 of our Building Adaptive Cards tutorial!**](/tutorials/building-adaptive-cards)

## Action.ShowCard

#### Description

This action will trigger a new card to appear within your current card. This is perfect if you don't want to show aspects of your card until the end user clicks on this action. A common use case for this is if a user wants to reject a card yet if they reject will have to supply a reason.&#x20;

Here's an example below:

```json
{
    "type": "AdaptiveCard",
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "body": [
        {
            "type": "Input.Text",
            "id": "input1",
            "placeholder": "Placeholder text"
        },
        {
            "type": "Input.Text",
            "id": "input2",
            "placeholder": "Placeholder text"
        }
    ],
    "version": "1.5",
    "actions": [
        {
            "style": "positive",
            "data": {
                "action": "APPROVE"
            },
            "title": "Approve",
            "type": "Action.Submit"
        },
        {
            "title": "Reject",
            "type": "Action.ShowCard",
            // nested card start
            "card": {
                "body": [
                    {
                        "isRequired": true,
                        "errorMessage": "You must select a rejection reason.",
                        "id": "choice",
                        "placeholder": "Rejection Reason",
                        "label": "Rejection Reason",
                        "choices": "${$root.payload.output.util_data}",
                        "type": "Input.ChoiceSet"
                    }
                ],
                "type": "AdaptiveCard",
                "actions": [
                    {
                        "data": {
                            "action": "REJECT"
                        },
                        "title": "Send",
                        "type": "Action.Submit"
                    }
                ],
                "version": "1.5"
            }
            // nested card end
        }
    ]
}
```

The above is a card with a "Reject" button that if clicked will show an [**Input.ChoiceSet**](https://academy.looply.ai/adaptive-cards/building-adaptive-cards/pages/NyuKVutiQ3m36qS4Iw7Q#input.choiceset) where they will have to select a value to send back to Looply in order to reject the card.

{% hint style="info" %}
Note: Data from inputs in this nested card will still be sent back to Looply as there is an [**Action.Submit**](#action.submit) button connected. Data sent to Looply will follow the same principles as shown in the [**Action.Submit**](#action.submit) section.
{% endhint %}

#### Technical Documentation

[**Read more about Action.ShowCard**](https://adaptivecards.io/explorer/Action.ShowCard.html)

## Action.ToggleVisiblity

#### Description

This is a simple action that will show and hide select elements within the adaptive card.

#### Technical Documentation

[**Read more about Action.ToggleVisibility**](https://adaptivecards.io/explorer/Action.ToggleVisibility.html)

## Action.Execute

#### Description

This action provides no use within Looply and should not be used. This action is useful if the card is implemented on an independent system from Microsoft Teams where a developer will independently make an invoke call to a Microsoft Bot/App. This button is identical in design to the [**Action.Submit**](#action.submit) action yet the data is not automatically sent anywhere as this process is independently implemented by the hosting app.

#### Technical Documentation

[**Read more about Action.Execute**](https://adaptivecards.io/explorer/Action.Execute.html)


# Data Binding

Bind data from your Looply Workflow directly into your Adaptive Card.

The data binding feature from the Adaptive Card designer allows the binding of any JSON attribute from the [**Sample Data Editor**](/adaptive-cards/building-adaptive-cards#sample-data-editor) to an element on an Adaptive Card.&#x20;

### Linking a Workflow to an Adaptive Card

When adding an Adaptive Card to a Workflow, if you click "Create new card" this will create a new Card but will also link the Workflow Schema to the Adaptive Card allowing you to bind for example the result from an API request to a [TextBlock](/adaptive-cards/building-adaptive-cards/content-elements#textblock) on your Adaptive Card. Find a more in-depth explanation from the [Using Integrations Section](/workflows/using-integrations) or check out [Chapter 1 of our Building Adaptive Cards tutorial](/tutorials/building-adaptive-cards).

### Simple Data Binding

Whilst in the Adaptive Card Designer you will notice a  [**Sample Data Editor**](/adaptive-cards/building-adaptive-cards#sample-data-editor)**,** this holds the data that can be bound to the Adaptive Card you are creating. Data can be added to this code editor or it can be pulled from the workflow schema where this card is bound. Below is a sample [**Sample Data Editor**](/adaptive-cards/building-adaptive-cards#sample-data-editor) that takes data from a **Workflow Schema**:

{% code fullWidth="false" %}

```json
{
    "payload": {
        "output": {
            "workflow_version": "1",
            "workspace_id": "****",
            "workflow_process_id": "****",
            "workflow_id": "****",
            "looply_sap_profile_settings": {
                "requires_sap_profile": false
            },
            "executor": "****",
            "organization_id": "****",
            "workflow_execution_id": "****",
            "workflow_trigger_type": "REQUEST"
        },
        "description": "Request"
    },
    "WorkflowStartState": null,
    "function_1": {
        "output": "hello world",
        "description": "My Function"
    }
}
```

{% endcode %}

Data binding can be done on a wide variety of properties ranging from the Text property of a [**TextBlock**](/adaptive-cards/building-adaptive-cards/content-elements#textblock) to the choices of an [**Input.ChoiceSet**](https://academy.looply.ai/adaptive-cards/pages/NyuKVutiQ3m36qS4Iw7Q#input.choiceset)**.**

Binding data from the [**Sample Data Editor**](/adaptive-cards/building-adaptive-cards#sample-data-editor) is easy. You can do it via any of these 3 ways:

1. When an element on the [**Adaptive Card Canvas**](/adaptive-cards/building-adaptive-cards#adaptive-card-canvas) has been selected, some elements like the [TextBlock](/adaptive-cards/building-adaptive-cards/content-elements#textblock) will show you a "Bind..." button. Clicking on this will pull up a dropdown menu showing you all the attributes you can bind to the [TextBlock](/adaptive-cards/building-adaptive-cards/content-elements#textblock). This dropdown is generated via the data supplied in the [**Sample Data Editor**](/adaptive-cards/building-adaptive-cards#sample-data-editor)**.**
2. When an element on the [**Adaptive Card Canvas**](/adaptive-cards/building-adaptive-cards#adaptive-card-canvas) has been selected, the [**Element Properties Toolbar**](/adaptive-cards/building-adaptive-cards#element-properties-toolbar) should have all the customisation options for the respective field. Notice that certain fields have a button displaying **"..."**, clicking on this will pull up a dropdown menu showing you all the attributes you can bind to this particular property. Like option 1, this dropdown is generated via the data supplied in the [**Sample Data Editor**](/adaptive-cards/building-adaptive-cards#sample-data-editor)**.**
3. You can manually type in the binding directly into the [**Card Payload Editor**](/adaptive-cards/building-adaptive-cards#card-payload-editor)**.**&#x20;

{% hint style="warning" %}
Note: Please note that if a binding is incorrectly typed directly into the [**Card Payload Editor**](/adaptive-cards/building-adaptive-cards#card-payload-editor), the adaptive card canvas may not appear.
{% endhint %}

Binding will appear in the [**Card Payload Editor**](/adaptive-cards/building-adaptive-cards#card-payload-editor) as shown below:

```json
{
    "type": "AdaptiveCard",
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "body": [
        {
            "type": "TextBlock",
            "wrap": true,
            "text": "${$root.payload.output.workflow_id}" // data binding
        }
    ],
    "version": "1.5"
}
```

{% hint style="warning" %}
Note: Manually typing in data bindings must follow the above syntax.
{% endhint %}

#### [Check out Chapter 7 of our Building Adaptive Cards tutorial!](/tutorials/building-adaptive-cards)

### Binding JSON Arrays

Data in our [**Sample Data Editor**](/adaptive-cards/building-adaptive-cards#sample-data-editor) may contain arrays of strings or arrays of JSON objects. Binding these structures to our Adaptive Card is a little different from the above [**Simple Data Binding**](#simple-data-binding) which follows a more one-to-one approach.&#x20;

{% hint style="warning" %}
Note: Manually typing data bindings from an array is highly advised. Using the [**Element Properties Toolbar**](/adaptive-cards/building-adaptive-cards#element-properties-toolbar) will result in incorrect mappings for array items.
{% endhint %}

If the data within the [**Sample Data Editor**](/adaptive-cards/building-adaptive-cards#sample-data-editor) looked as follows:

```json
{
    "payload": {
        "output": {
            "process_id": "****",
            "workflow_id": "****",
            "workflow_execution_id": "****",
            "version": "5",
            "scenario_id": "****",
            "workflow_version": "1",
            "workspace_id": "****",
            "util_data": [ // array of JSON objects to bind
                {
                    "title": "Claim not approved",
                    "value": "1"
                },
                {
                    "title": "Query with claim",
                    "value": "2"
                },
                {
                    "title": "Incorrect grade / SCP claimed",
                    "value": "3"
                },
                {
                    "title": "Incorrect payment type claimed",
                    "value": "4"
                }
            ],
            "organization_id": "****",
            "workflow_trigger_type": "REQUEST",
            "recipient": "email@example.COM",
            "step": "****"
        },
        "description": "Request"
    }
}
```

#### Displaying Singular Items.

Displaying only the 3rd `title` from our `util_data` array is straightforward and replicates process 3 from [Simple Data Binding](#simple-data-binding).&#x20;

```json
{
    "type": "AdaptiveCard",
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "version": "1.5",
    "body": [
        {
            "type": "TextBlock",
            "wrap": true,
            "text": "${$root.payload.output.util_data[2].title}" // data binding
        }
    ]
}
```

#### Displaying Lists

If we want to display a [**TextBlock**](/adaptive-cards/building-adaptive-cards/content-elements#textblock) for each of the `title` values in our `util_data` array we need to manually type this binding into our [**Card Payload Editor**](/adaptive-cards/building-adaptive-cards#card-payload-editor) against our [**TextBlock**](/adaptive-cards/building-adaptive-cards/content-elements#textblock)**.** A "**data context**" has to be established on our [**TextBlock**](/adaptive-cards/building-adaptive-cards/content-elements#textblock) - this means that the [**TextBlock**](/adaptive-cards/building-adaptive-cards/content-elements#textblock) is only observing data within a certain attribute within our [**Sample Data Editor**](/adaptive-cards/building-adaptive-cards#sample-data-editor)**.** Due to a data context being applied to the [**TextBlock**](/adaptive-cards/building-adaptive-cards/content-elements#textblock)**,** our binding will appear shorter than the above [Simple Data Binding](#simple-data-binding) approach. Below is how we can display 8 TextBlocks, where each element points to a different `title` in our `util_data` array.

```json
{
    "type": "AdaptiveCard",
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "version": "1.5",
    "body": [
        {
            "type": "TextBlock",
            "wrap": true,
            "$data": "${$root.payload.output.util_data}", // data context
            "text": "${title}" // data binding
        }
    ]
}
```

{% hint style="info" %}
Note: Binding data using a "**data context**" will stop the Adaptive Card Designer from immediately showing the final rendered card. You will have to use the **Test Card** action to see the final result - [Card Toolbar](/adaptive-cards/building-adaptive-cards#card-toolbar) then: View>Test Card.
{% endhint %}

#### Using JSON Arrays in Input.ChoiceSets

You can bind data directly to the `choices` attribute of an [Input.ChoiceSet](https://academy.looply.ai/adaptive-cards/pages/NyuKVutiQ3m36qS4Iw7Q#input.choiceset). This is particularly useful if the choices are dynamic and could change.&#x20;

```json
{
    "type": "AdaptiveCard",
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "version": "1.5",
    "body": [
        {
            "type": "Input.ChoiceSet",
            "choices": "${$root.payload.output.util_data}", // data binding
            "placeholder": "Choices"
        }
    ]
}
```

{% hint style="info" %}
Note: This works due to the structure of `util_data` replicating that of an [Input.ChoiceSet](https://academy.looply.ai/adaptive-cards/pages/NyuKVutiQ3m36qS4Iw7Q#input.choiceset) `choices` attribute array. Always test your card using the **Test Card** action in the **View** menu of the [Card Toolbar](/adaptive-cards/building-adaptive-cards#card-toolbar) to check the binding has worked.
{% endhint %}

#### [Check out Chapter 8 of our Building Adaptive Cards tutorial!](/tutorials/building-adaptive-cards)


# Conditional Rendering

Render select elements depending on the cards current data payload.

Different Payloads will be used against adaptive cards depending on the results from the previous states within a Looply Workflow. Different results may require you to change the card to accommodate these changes. e.g. an error message from a previous state in the Looply Workflow.

{% hint style="info" %}
Note: Using the [**Card Payload Editor**](/adaptive-cards/building-adaptive-cards#card-payload-editor) is the easiest method to write the logic for conditional rendering.
{% endhint %}

### Overview

Each element has an **"Only show when"** attribute which is rendered as the `$when` attribute in the [**Card Payload Editor**](/adaptive-cards/building-adaptive-cards#card-payload-editor)**.** If this attribute is `true`, will display the element.&#x20;

Each element has an **"Initially Visible"** attribute which is rendered as the `isVisible` attribute in the [**Card Payload Editor**](/adaptive-cards/building-adaptive-cards#card-payload-editor)**.** If this attribute is `true`, will display the element when the card first loads. We will not go into detail with this option as it is not as powerful as the `$when` attribute as described above.

Adding a condition to trigger a `true` or `false` value for the `$when` takes principles described in the [Data Binding](/adaptive-cards/data-binding) section and standard logical operations commonly seen in programming languages such as JavaScript.&#x20;

{% hint style="info" %}
Note: Due to the conditions being written in a JSON format they need to be encased in a string. Checking if a string equals another will require you to use `\` to escape the quotes special character.
{% endhint %}

### Conditionally Rendering an Error Message

If you were to get an error from your Looply Workflow. We may need to display a red banner and a message about what has gone wrong. But we should only display this if we get an error otherwise we should display a success message.

An example payload from a Looply Workflow could look like the following:

```json
{
    "httpRequestSAP_1": {
        "output": {
            "headers": {
                "www-authenticate": "Bearer realm=\"SAP NetWeaver Application Server [GW1/100]\", error=\"invalid_request\", error_description=\"Both, OAuth 2.0 token endpoint and resource server require usage of TLS 1.0. Caller used HTTP instead of HTTPS\"",
                "set-cookie": "sap-usercontext=sap-client=100; path=/",
                "content-length": "0",
                "content-type": "text/html",
                "sap-server": "true"
            },
            "data": "CSRF Token fetch failed",
            "status": "400",
            "statusText": "Bad Request"
        },
        "description": "SAP Update"
    }
}
```

We want to conditionally render the message at `httpRequestSAP_1.output.data` if the status is `400` or `500`. Below is an example of the card payload from the [**Card Payload Editor**](/adaptive-cards/building-adaptive-cards#card-payload-editor) to make this happen:

```json
{
    "type": "AdaptiveCard",
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "version": "1.5",
    "body": [
        {
            "type": "Container",
            "items": [
                {
                    "type": "TextBlock",
                    "text": "SUCCESS",
                    "wrap": true
                }
            ],
            "style": "good",
            // render the success message if the status does not equal 400
            "$when": "${$root.httpRequestSAP_1.output.status != \"400\"}" 
        },
        {
            "type": "Container",
            "items": [
                {
                    "type": "TextBlock",
                    "text": "ERROR - ${$root.httpRequestSAP_1.output.data}",
                    "wrap": true
                }
            ],
            "style": "attention",
            // render the error message if the status equals 400 or 500
            "$when": "${$root.httpRequestSAP_1.output.status == \"400\" || $root.httpRequestSAP_1.output.status == \"500\"}"
        }
    ]
}
```

All conditions are encased in `${}` much like that of [data binding](/adaptive-cards/data-binding).&#x20;

{% hint style="info" %}
Note: Similar to the binding in the [Binding JSON Arrays](/adaptive-cards/data-binding#binding-json-arrays) section. We can only see the result by testing the card using the Test Card action in the View menu from the [Card Toolbar](/adaptive-cards/building-adaptive-cards#card-toolbar).
{% endhint %}

### Logical Operators

As described above, the logical operators that are available are similar to those found in some programming languages. Below is a list:

| Operator | Operation                |
| -------- | ------------------------ |
| &&       | AND                      |
| \|\|     | OR                       |
| \\       | Escape Special Character |
| !=       | Not Equals               |
| ==       | Equals                   |

### Prebuilt Functions

You can make use of a number of prebuilt functions in your conditional rendering logic. For more information, visit [this link](https://learn.microsoft.com/en-us/azure/bot-service/adaptive-expressions/adaptive-expressions-prebuilt-functions?view=azure-bot-service-4.0).&#x20;

#### [Check out Chapter 9 of our Building Adaptive Cards tutorial!](/tutorials/building-adaptive-cards)


# AI Assistant

Harness the power of ChatGPT to quickly create Adaptive Cards.

Ask ChatGPT to create any kind of card you would like, from an expense card that needs approval to a notification that needs to be sent to a manager.

#### Where?

Click on the **Help** menu from the [Card Toolbar](/adaptive-cards/building-adaptive-cards#card-toolbar). Then select AI Assistant.&#x20;

#### What do I ask?

Ask anything relating to adaptive cards, if you are missing information out from your prompt then ChatGPT will ask for more information.

{% hint style="info" %}
Note: This model of ChatGPT explicitly works to create Adaptive Cards and will only respond in relation to this topic.
{% endhint %}

### Using ChatGPT Designed Cards

If Looply recognizes an Adaptive Card has been sent back from ChatGPT then Looply will render it within the chat panel. Below the card will feature a button that will either display **"Use Card in Designer"** if you are currently working in the Adaptive Card Designer or **"Create a new card**" if you're in the Card Vault/Adaptive Card Designer Overview screen.

You can continue to refine an Adaptive Card by continually prompting ChatGPT with changes you wish to make.

#### [**Check out our Adaptive Cards with AI tutorial!**](/tutorials/adaptive-cards-with-ai)


# Inline Functions

The Adaptive Card Designer has a large selection of functions for inline formatting of data.

The Adaptive Card Designer is great for formatting or doing complex calculations using in-line functions directly on the elements you have used to create your card. This could be a complex mathematical formula where the answer is dynamically calculated and displayed directly in a [**TextBlock**](/adaptive-cards/building-adaptive-cards/content-elements#textblock) or format a timestamp into an easier-to-read format as soon as the card is rendered. These inline functions follow the same principles from the [**Data Binding**](/adaptive-cards/data-binding) and/or the [**Conditional Rendering**](/adaptive-cards/conditional-rendering) sections.

{% hint style="info" %}
Note: As this follows the same principles as [**Data Binding**](/adaptive-cards/data-binding) and [**Conditional Rendering**](/adaptive-cards/conditional-rendering), it is highly recommended to implement these inline functions directly into the [**Card Payload Editor**](/adaptive-cards/building-adaptive-cards#card-payload-editor). This is the easiest approach.
{% endhint %}

There is a wide variety of Inline Functions available to use, [**see here**](https://learn.microsoft.com/en-us/azure/bot-service/adaptive-expressions/adaptive-expressions-prebuilt-functions?view=azure-bot-service-4.0).

## Format Timestamp Example

The following example will describe how to convert a date/timestamp from a `DD-MM-YYYY hh:mm:ss` format to a `YYYY-MM-DD` format as commonly seen in SAP. We can do all this using inline functions within the Adaptive Card Designer. Below is a sample JSON from a Looply Workflow:

```json
{
    "payload": {
        "output": {
            "date": "03/15/2018 12:00:00" // date to format
        },
        "description": "Request"
    }
}
```

Next is the Adaptive Card JSON which will format this date into `YYYY-MM-DD` format.

```json
{
    "type": "AdaptiveCard",
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "body": [
        {
            "type": "TextBlock",
            "wrap": true,
            // inline function
            "text": "${formatDateTime($root.payload.output.date, 'yyyy-MM-dd')}"
        }
    ],
    "version": "1.5"
}
```

{% hint style="info" %}
Note: Different inline functions will require different parameters but will always be triggered the same if wrapped in `${}.`
{% endhint %}


# Building Workflows

Looply workflows are tailored to empower developers with the tools they need to streamline and automate your business processes

## Introduction

At the heart of Looply lies the transformative potential of workflow automation, a tool designed to streamline operations, reduce manual tasks, and enhance productivity across your organisation.&#x20;

Unlock this potential with Looply Academy, providing you with the knowledge and tools needed to construct efficient, automated processes tailored to your unique business needs.

## Use Cases

Here are some common use cases you can automate with Looply Workflows:

<details>

<summary>Automated Employee Onboarding</summary>

Once a new employee’s data is entered into the HR system, Looply can orchestrate a sequence of tasks — from sending welcome emails, populating them in relevant project management tools, to initiating IT provisioning requests.

</details>

<details>

<summary>Sales Report Consolidating</summary>

For sales teams using multiple platforms to track leads, Looply can amalgamate data from all these sources at the end of the week, process this information, and present consolidated reports in a Teams channel.

</details>

<details>

<summary>Expense Validator</summary>

Whenever an expense entry is made in platforms like Expensify or Concur, Looply can validate this against set criteria and then either automatically approve standard expenses or route exceptions to managers via Teams.

</details>

<details>

<summary>Inventory Alert System</summary>

In retail, if stock levels in the inventory system fall below a certain threshold, Looply can generate alerts, send purchase order requests to suppliers, and notify the procurement team in Teams.

</details>

<details>

<summary>Customer Feedback Loop</summary>

After a customer service interaction, Looply can dispatch a feedback survey. Based on the feedback, either a thank-you message can be automated for positive responses, or in the case of negative feedback, an escalation notice is sent to the customer service head.

</details>

<details>

<summary>Procurement Auto-Fill</summary>

A bot that peruses incoming emails for invoices, extracts details, populates them in a procurement platform and then notifies the finance team for validation.

</details>

<details>

<summary>Attendance Bot</summary>

Merging attendance data from biometric systems with HRMS platforms, so if an employee misses a punch-in, they receive a prompt on MS Teams.

</details>

<details>

<summary>Feedback Aggregator</summary>

After training sessions, Looply can aggregate feedback from Google Forms, categorize comments, and present concise summaries in MS Teams channels.

</details>

## Creating Workflows

You can get started creating a new workflow by navigating to the **Workflow Studio** and clicking the **Create** button.&#x20;

Enter an appropriate name for your workflow in the dialog and click **Create** to continue to the Workflow Builder.&#x20;

<figure><img src="/files/VrZlI3ViSYm2jSGjLDa1" alt=""><figcaption></figcaption></figure>

### Using the Workflow Builder

#### Configuring Event Trigger

The first step you'll need to carry out is to configure how your workflow should be executed.&#x20;

Looply currently supports 2 workflow triggers:&#x20;

* HTTP Requests
* Schedules

<figure><img src="/files/KgqyEmLWX9IuuzI3TeRG" alt=""><figcaption></figcaption></figure>

See [Triggering Workflows](/workflows/triggering-workflows) for more information on workflow triggers.&#x20;

#### Adding Steps

After setting up your event trigger, you're ready to start adding steps and building out your workflow.&#x20;

To add a new step to your workflow, click the **+** button located under any step and select the new step you would like to add from the right panel **Toolbox**.&#x20;

<figure><img src="/files/dlkTAUO8exoaNfNWNu53" alt="" width="363"><figcaption></figcaption></figure>

#### Deleting Steps

You can delete steps in your workflow by simply clicking on any step you wish to delete, and then clicking the **Delete** button located in the right side panel.&#x20;

### Saving Changes

You can save changes to your workflow at any time by clicking the **Save** button located at the top of the page.&#x20;

{% hint style="info" %}
**Note:** If you attempt to save changes on a workflow that has been activated, a new version will automatically be created for you to prevent breaking any processes using the previous live version.&#x20;
{% endhint %}

## Testing Workflows

Steps within your workflow that are expected to return a response, such as HTTP Requests, Functions and Conditionals, can be tested individually to ensure they are working correctly and provide wider access to the exact response data.&#x20;

When you execute a test of an individual step within your workflow, the response is stored and can be accessed by any subsequent steps to allow support for data passing.&#x20;

#### Testing Steps

You can perform a test of a specific step in your workflow by selecting the step from the canvas and clicking the **Test Step** button located in the right-side panel header.&#x20;

We will attempt to execute your step with the current step data and display the response.&#x20;

<figure><img src="/files/LHyavJpaefTWENFRttZw" alt=""><figcaption></figcaption></figure>

#### Executing Workflows

You can test your entire workflow from start to finish by executing it from within the Workflow Builder.&#x20;

To execute your workflow, make sure you have saved your most recent changes and then select the overflow menu button, and choose the **Execute** option.&#x20;

For workflows triggered by an HTTP Request, if you have configured your payload data then this will be automatically passed in and used as mock data for the test execution.&#x20;

<figure><img src="/files/vwPMZoKPtFukLELQdm45" alt="" width="563"><figcaption></figcaption></figure>

## Activating Workflows

Once you have finalised your workflow, you can activate your workflow by clicking the **Activate** button located at the top of the page.&#x20;

Activating a workflow will prevent any further changes from being made to the current version - and force changes to be pushed onto a new version to protect any production systems using it.&#x20;


# Triggering Workflows

Define how your Looply Workflows are initiated with event triggers

Every workflow within Looply is required to have a defined event trigger as the first step to determine how the process will be initiated. Here are some triggers we currently support:&#x20;

* **HTTP** **Request** - triggers your workflow when an HTTP request is received from your system or client
* **Scheduled** - triggers your workflow on a preset schedule defined by you

<figure><img src="/files/KgqyEmLWX9IuuzI3TeRG" alt=""><figcaption></figcaption></figure>

## Trigger via HTTP Request

Request event triggers can be used when you want to be able to trigger an execution of your workflow by sending an **HTTP Request** to the Looply API. These event triggers also support attaching a body payload to your request which will be passed into your workflow at runtime.&#x20;

### Configure Payload

Workflows set up to use an HTTP Request as the trigger also supports the configuration of a mock payload during workflow development.&#x20;

Mock payloads work by allowing the developer to send a POST request to a unique Looply API with data in the body that will resemble real-world data your workflow is expected to receive during runtime. This mock data is then stored against your workflow and can be accessed by steps within your workflow or will be attached during test executions.&#x20;

#### Send Request

You can find the unique Looply API endpoint for receiving data for your workflow by clicking on your event trigger step and opening it in the side panel.&#x20;

You will find your unique endpoint under the **Request URL** field and can copy this to clipboard with the **Copy** button.&#x20;

Now all you need to do is use a REST client of your choice to send a POST request to this endpoint and setup your mock data in the body.&#x20;

#### Detecting Payload

Once you have sent at least 1 test request to your unique endpoint, you can sync the data to your workflow by clicking on the **Detect Payload** button.&#x20;

Your mock payload will be displayed within this side panel for you to confirm.&#x20;

<figure><img src="/files/AZDgJlQZze1J9ipP3OKB" alt="" width="378"><figcaption></figcaption></figure>

## Scheduling Workflows

Scheduled workflows allow you to run automated workflows at fixed intervals without manual intervention. These are ideal for tasks like data cleanups, reporting, syncing external systems, or sending reminders on a recurring basis.

Because scheduled workflows are executed automatically, they **do not support a dynamic payload at runtime**. Instead, they rely on static configurations - making environment profiles particularly useful.

<figure><img src="/files/bAoVkb7JElr7AJHS01n1" alt=""><figcaption></figcaption></figure>

### Supported Scheduling Options

You can configure a scheduled workflow to run at any of the following intervals:

* **Minutes**
* **Hourly**
* **Daily**
* **Weekly**

### Using Execution Profiles with Scheduled Workflows

When setting up a schedule, you can now **specify an environment variable profile** to be used at runtime. This allows you to inject static, environment-specific data into your workflow payload - ensuring it behaves correctly across different environments (e.g. `dev`, `qa`, or `prod`).

If no profile is specified, the **default profile** (if set) will be used automatically.

#### Automatic Start on Activation

Once a schedule is configured and your workflow is **activated**, it will begin executing according to the schedule immediately - using the selected execution profile for each run.

### Pause/Resume Schedules

Once activated, scheduled workflows can be paused at any given time by visiting the Workflow Builder, selecting the Event Trigger step, and then clicking the **Pause** button.&#x20;

Paused workflows can be resumed at any time by repeating the same process as above and clicking the **Resume** button.&#x20;

{% hint style="info" %}
**Note:** You can only have one version of your scheduled workflow active at any given time. If you create a new version of your scheduled workflow and activate it - the previous version schedule will be canceled.&#x20;
{% endhint %}


# Environment Variables & Profiles

Define workflow environment-specific variables to keep your logic clean, flexible, and reusable.

Workflow **Environment Variables** in Looply allow you to define reusable sets of values that can be injected into your workflow at runtime. Each environment consists of a **profile -** a JSON object containing key-value pairs - that can be used to dynamically configure workflow behaviour. You can create multiple profiles per workflow, select which one to use at runtime, and set a **default profile** to fall back on when no specific selection is made. This helps keep your workflows clean, flexible, and easy to maintain across different environments or use cases.

## Managing & Using Environment Variable Profiles

Environment variables in Looply make it easy to configure workflow behaviour dynamically - without hardcoding values into each step. With environment profiles, you can switch between sets of values tailored for different environments like `dev`, `qa`, `staging`, or `prod`.

<figure><img src="/files/5h7KIWyKO3OupwQj1c3k" alt=""><figcaption></figcaption></figure>

### Creating & Managing Profiles

To manage environment variable profiles:

1. Open your workflow in the **Workflow Studio**.
2. Click the **workflow settings menu (⋮)** and select **Environment Variables**.
3. In this section, you can:
   * **Create** new profiles for different environments.
   * **Edit** each profile’s JSON data to suit the needs of that environment.
   * **Set a default profile**, which is used when no other profile is specified.
   * **Rename** existing profiles.
   * **Duplicate** profiles
   * **Delete** profiles that are no longer needed.

Each profile is essentially a JSON object of key-value pairs that will be injected into the workflow’s payload at runtime.

### Selecting a Profile for Execution

<figure><img src="/files/qcveYsw2LNVyG8eThsEP" alt=""><figcaption></figcaption></figure>

Once profiles are set up, executing a workflow with a specific profile is easy:

1. Click **Execute** in the top-right of your workflow.
2. A dialog will appear prompting you to select an **Execution Profile** from a dropdown.
3. Choose the profile you want (e.g. `prod`, `staging`, `dev`) and click **Execute**.

The selected profile’s environment variable data will be injected into the payload and accessible to all steps in the workflow.

{% hint style="info" %}
💡 **Tip:** You can also reference environment variables as parameters in function steps, conditions, or integrations using the workflow data binding menu.
{% endhint %}

#### Developer API

For advanced usage or automated execution, environment variable profiles can also be selected programmatically via the **Developer API**. See the Developer API documentation for details on how to pass a profile name as part of your workflow execution payload.

{% content-ref url="/pages/s4gM4N2pksrevybhfCC6" %}
[Workflow API](/api-reference/workflow-api)
{% endcontent-ref %}


# Versioning Workflows

Manage and track changes with ease with the power of workflow versioning at your fingertips

Workflow versioning is a critical feature that empowers users to manage changes, updates, and enhancements to their workflows with precision and control.&#x20;

By creating versions of your workflows, you can ensure that improvements are made safely, test new configurations without disrupting ongoing operations, and easily roll back to previous versions if needed.

### Version Statuses

Each version of a workflow within Looply is assigned a unique status, a fundamental aspect of our version control system.&#x20;

When a workflow is in its Draft phase, it grants you the flexibility to freely modify and test your processes without impacting any operational Looply processes that utilise the most current version.&#x20;

Upon finalising your adjustments, activating the workflow solidifies its status, thereby securing it against further modifications. Activation ensures that this particular version becomes the default for Looply processes configured to utilise the latest workflow version, maintaining system integrity and operational continuity.

For more information on triggering specific workflow versions see [Triggering Workflows](/workflows/triggering-workflows)

### Creating New Versions

You can get started creating a new version for your workflow by navigating to the latest active version, making some changes and then saving your workflow.&#x20;

As the workflow is active, you will be prompted to confirm your changes and agree that a new version will be created in order to prevent potentially breaking changes to the existing workflow version. Once you have confirmed this, your workflow will immediately be versioned up and placed into Draft status to continue working on your changes.&#x20;

### Locking Versions

Once you have made the necessary changes to your workflow and are ready to go live, you can activate your workflow by clicking the **Activate** button.&#x20;

This will prevent any further changes from being allowed on this version of your workflow and will ensure this version is now picked up as the latest.&#x20;


# Using HTTP Requests

Sending HTTP Requests in your Looply Workflow

## Configuring Requests

You can get started configuring your request by clicking on a **+** button in your workflow and selecting the **HTTP Request** step from the Workflow's toolbox.  &#x20;

<figure><img src="/files/l32etOJ9g6uuCOWiyXDA" alt=""><figcaption></figcaption></figure>

Requests sent from Looply support the following configuration options:&#x20;

* Request URL
* Request Type
  * `GET`
  * `POST`
  * `PUT`
  * `PATCH`
  * `DELETE`
* URL Parameters
* Headers
* Body Type
  * `JSON`
  * `multipart/form-data`
  * `x-www-form-urlencoded`
  * `XML`
  * `Raw`
* Body
* Authorization
  * `Bearer Token`
  * `Basic Auth`

<figure><img src="/files/1VPIHvlGsdpopmynCbTb" alt=""><figcaption></figcaption></figure>

### Request

To send an HTTP request, start by selecting the desired **request type** from the dropdown menu. Choose from `GET`, `POST`, `PUT`, `PATCH`, or `DELETE`. Then, in the **Request URL** text field, enter the accurate destination URL to which you want to send the request. Make sure the URL is correctly formatted to ensure successful communication.

<figure><img src="/files/rR4kA1e40QQHyIJK7Ffy" alt=""><figcaption></figcaption></figure>

#### URL Parameters

Users are also able to add query string parameters in their request if they require them under the **URL Parameters** section.&#x20;

Add specific query string parameters by entering their names and values in the corresponding **Key** and **Value** inputs. Need more parameters? Simply click **+** **Add parameter** for additional sets of inputs. This flexibility empowers you to tailor your request to meet specific criteria.

#### Headers

Additionally, users can specify specific headers they wish to attach to their request under the **Headers** section.&#x20;

Add headers by entering their names and values in the corresponding **Key** and **Value** inputs. Click the **+** **Add header** button for inserting more than one header.&#x20;

### Payload

The **Body** section allows you to define the payload of your HTTP request. Select the appropriate 'Body Type' from the dropdown menu - `JSON`, `multipart/form-data`, `x-www-urlencoded`, `XML`, or `Raw`.&#x20;

<figure><img src="/files/eDjLXlU0LpJV6pfsvF6g" alt=""><figcaption></figcaption></figure>

For `JSON`, `multipart/form-data`, and `x-www-urlencoded`, use the **key-value pair inputs** to finely tune your payload - using the **+ Add item** button to insert more than one data item.&#x20;

Alternatively, for `XML` or `Raw` data, leverage the **text area** to input your content directly.&#x20;

### Authorization

HTTP requests executed by Looply can be authorised through the use of **Bearer Tokens**, **Basic Auth** (username and password) and Looply **Integration** credentials.&#x20;

<figure><img src="/files/XY02x9nKSrIUf0fAZoeG" alt=""><figcaption></figcaption></figure>

#### Bearer Tokens

To attach a bearer token to your HTTP Request, select the `Bearer Token` option in the dropdown menu under the **Authorization** section.&#x20;

A single text field will be displayed below for you to enter your bearer token string.&#x20;

#### Basic Auth

To use basic username and password authentication with your HTTP Requests, select the `Basic Auth` option from the dropdown menu.&#x20;

Enter a username and password to be used in the relevant text fields.&#x20;

#### Integration

HTTP Requests can be authenticated with the authentication credentials of an existing connected or configured integration within Looply. For example, this could be through the bearer token retrieved from an integration made by your organisation admin, client credentials or API keys.&#x20;

To use integration authentication, select the Integration option from the dropdown menu.&#x20;

Access the integration selection dropdown menu to view all existing connected or configured integrations with a supported authentication method for your request, and select the integration to attach it.&#x20;

{% hint style="info" %}
**Tip:** You can also select an integration to use for authenticating the request by binding the selection field to the id of either the integration or profile - which is visible on the configure integration profile page. Simply, select the `Use a variable` option when selecting an integration and bind it to your data in your workflow. This allows the conditional selection of an integration based on your workflow data and the integration is automatically picked up at runtime.                          &#x20;
{% endhint %}

### Failure Scenarios

Navigate through potential hiccups effortlessly - Looply equips you with options to gracefully handle failures in your HTTP Requests, offering the ability to continue on failure and automatic retry functionalities.

<figure><img src="/files/Bqtslo4AhvqCWmv9YaEz" alt=""><figcaption></figcaption></figure>

#### Continue on Failure

Looply supports the ability to continue executing your workflow in the event the HTTP Request step fails. This can be useful if you wish to manually handle any errors or if your workflow is not reliant on the response from the request.&#x20;

This functionality can be enabled by toggling the **Continue if request fails** switch within the HTTP Request editor.&#x20;

#### Automatic Retry

Additionally, users can enable automatic retries for their HTTP Request where in the event of request failure - Looply will automatically attempt the request again. This feature is currently limited to a maximum of 100 attempts before the step will fail.&#x20;

To enable automatic retries, toggle the **Auto-retry** switch within the HTTP Request editor.&#x20;

## Testing Requests

HTTP Requests can be effortlessly validated by configuring your request data and using the Test Step feature.&#x20;

Simply click the **Test Step** button in the header of the HTTP Request editor, execute your request, and receive instant feedback.&#x20;

The response status and any returned output will be displayed, ensuring a seamless testing experience within the confines of your workflow.


# Using Functions

Executing custom function blocks in Looply workflows

Unlock the potential of custom functionality in your workflows with Looply's powerful function steps. Tailor your processes by creating functions, defining parameters, and writing custom JavaScript code. Seamlessly integrate your logic and tap into pre-defined libraries, empowering you to elevate your application experience.&#x20;

Whether you need to automate complex tasks, manipulate data, or integrate with external services, these custom function steps offer unparalleled flexibility to meet your unique workflow requirements.

## Configuring Functions

You can get started configuring your function by clicking on a **+** button in your workflow and selecting the **Function** step from the Workflow's toolbox.  &#x20;

<figure><img src="/files/bJOMl8vIyL9UxoHoGh6g" alt=""><figcaption></figcaption></figure>

### Passing Parameters

Looply empowers you to effortlessly pass custom data parameters to your function code, providing a seamless way to fine-tune and optimize the operation of your custom functions.

Configure your parameters by adding a name and value for each parameter, tailoring the data to your specific needs. Need more parameters? No problem. Click the **+** **Add parameter** button to add more.

Users can decide to manually enter static values for each parameter, or use the dropdown menu to select a value from a previous step in your workflow.&#x20;

<figure><img src="/files/pi5c9G5MDN54UEeag1Ub" alt=""><figcaption></figcaption></figure>

### Writing JavaScript Code

Use the integrated Monaco code editor to write your custom JavaScript inside the `myFunction()` code block. **Data returned from here will be returned for further use within your workflow.**&#x20;

The `myFunction` code block is designed to accept a `params` object, which will contain all of your previously configured parameter data. You can use this object to access your parameter data by referencing `params.your_parameter` in your code.&#x20;

For example, if you configured a parameter with the name `id` then it's value could be accessed within `myFunction` by referencing `params.id`.&#x20;

### Using Libraries

Similarly to parameters, our workflow functions also support access to a range of useful JavaScript libraries to assist you.&#x20;

The `myFunction` code block has access to a second parameter called `libraries`.&#x20;

You can access any of our JavaScript libraries through this object by referencing `libraries.library_name`.&#x20;

Here is an example of how you could use the UUID JavaScript library within your function:&#x20;

{% tabs %}
{% tab title="Using UUID Example" %}
{% code overflow="wrap" %}

```javascript
function myFunction(params, libraries) {
    const uuid = libaries.uuid; 
    const id = uuid.v4(); 
    return id; 
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

## Testing Functions

Functions can be effortlessly validated by configuring your function and using the Test Step feature.&#x20;

Simply click the **Test Step** button in the header of the Function editor, execute your code, and receive instant feedback.&#x20;

Any returned output will be displayed, ensuring a seamless testing experience within the confines of your workflow.


# Using Conditionals

Using conditional logic in Looply workflows

Dictate the flow of your processes with precision using Looply's conditional steps.&#x20;

Conditionals operate in an if-else fashion, allowing users to define a series of conditions to evaluate and then seamlessly branch workflows into two distinct paths based on true or false outcomes.&#x20;

Whether crafting your own static conditions or evaluating data returned from a prior workflow step, these conditional steps offer unparalleled control and flexibility in shaping the logic of your workflows.

## Define Conditional Logic

You can get started adding your conditional logic by clicking on a **+** button in your workflow and selecting the **Conditional** step from the Workflow's toolbox.  &#x20;

This will add the conditional step to your workflow and branch out into 2 separate flows for **Yes** and **No** outcomes. Each new branch will contain a placeholder until you are ready to determine your next steps.&#x20;

<figure><img src="/files/VPtKH6bKfcrJmUABtFsV" alt=""><figcaption></figcaption></figure>

### Adding Rules

You can use the **Rules** section of the Conditional editor to define the specific conditions to evaluate as true for your condition to pass.&#x20;

<figure><img src="/files/esQTTN2O1QlBO8NXg2K6" alt=""><figcaption></figcaption></figure>

Each rule contains a **variable name**, a **condition**, and a **value**.&#x20;

Conditions we support are:&#x20;

* Boolean Equals
* Is Boolean
* Is Null
* Is Numeric
* Is Present
* Is String
* Is Timestamp
* Not
* Numeric Equals
* Numeric Greater Than
* Numeric Greater Than Equals
* Numeric Less Than
* Numeric Less Than Equals
* String Equals
* String Greater Than
* String Greater Than Equals
* String Less Than
* String Less Than Equals
* String Matches
* Timestamp Equals
* Timestamp Greater Than
* Timestamp Greater Than Equals
* Timestamp Less Than
* Timestamp Less Than Equals

Create your rule by manually entering or selecting the variable that should be evaluated, followed by the conditional operator, and finally manually entering or selecting the expected value.&#x20;

You can add more than one rule by clicking the **+ Add rule** button and repeating this process.&#x20;

Additionally, you can access the dropdown menu in the Variable and Value fields to select data from previous steps in your workflow for use in your rule.&#x20;

{% hint style="info" %}
**Note:** Remember when adding rules, **all of your rules** must be true for your conditional logic to pass and follow down the **Yes** branch of your workflow.&#x20;
{% endhint %}

## Handling Outcomes

Once you have added all of your conditional logic, the final step is to handle what your workflow should do next depending on the outcome of your condition. &#x20;

Conditional steps are designed to branch out into two flows for Yes (true) and No (false) outcomes of your logic. Each branch is initialized with a placeholder step.  &#x20;

You can determine the next steps for your workflow by clicking on the placeholder step, and then selecting the new step you want to insert in its place for each outcome.&#x20;

{% hint style="info" %}
**Note:** Deleting conditional steps within your workflow will result in both outcome branches and all subsequent steps also being deleted.&#x20;
{% endhint %}

<figure><img src="/files/ySBy3C7YQGJolfFgijMf" alt=""><figcaption></figcaption></figure>


# Using Branch Conditionals

Using branch conditionals in Looply workflows

Branch conditionals empower users to set a single variable and conditional operator rule, and then create multiple branches of outcomes tailored to the values evaluated against the specified condition.&#x20;

With the ability to define diverse paths based on varying values or outcomes, these Branch Conditional Steps elevate the sophistication of your workflows, providing a nuanced approach to shaping the flow of your processes.

## Define Conditional Logic

You can get started adding your conditional logic by clicking on a **+** button in your workflow and selecting the **Branch** **Conditional** step from the Workflow's toolbox.  &#x20;

<figure><img src="/files/ymMYByvfsIvnZpq7UL68" alt=""><figcaption></figcaption></figure>

### Add Condition

Branch Conditionals work differently from standard Conditionals, in that they only support one single condition being defined but can have multiple different value outcomes.&#x20;

You can define your single conditional rule by entering a variable in the text field and selecting a conditional operator from the dropdown menu.&#x20;

Conditions we support are:&#x20;

* Boolean Equals
* Is Boolean
* Is Null
* Is Numeric
* Is Present
* Is String
* Is Timestamp
* Not
* Numeric Equals
* Numeric Greater Than
* Numeric Greater Than Equals
* Numeric Less Than
* Numeric Less Than Equals
* String Equals
* String Greater Than
* String Greater Than Equals
* String Less Than
* String Less Than Equals
* String Matches
* Timestamp Equals
* Timestamp Greater Than
* Timestamp Greater Than Equals
* Timestamp Less Than
* Timestamp Less Than Equals

<figure><img src="/files/BQnqoArgFWjuyBUR7bS0" alt=""><figcaption></figcaption></figure>

### Creating Value Branches

Once you have defined your conditional rule, click and drag from the icon attached to the Branch Conditional step within the workflow to create a new value branch.&#x20;

The Create Branch dialog will open and allow you to specify a:&#x20;

* Label - the user-friendly name for your outcome
* Value - the value that will be evaluated against the condition
* Type - the type of outcome being handled (`Default`, `Positive`, `Negative`, `Warning` or `Info`)

Enter these values for your branch and click **Create**.&#x20;

You can create as many branches as required for your condition to handle different outcomes.&#x20;

<figure><img src="/files/ZQQvFBmOt5jDyUqjCXOP" alt=""><figcaption></figcaption></figure>

#### Deleting Branches

Branches can be edited by simply clicking on the branch and editing its values in the sidebar window or removed by clicking the **Delete** button.&#x20;

<figure><img src="/files/ysTJeOmAuiaqkuZAxft6" alt=""><figcaption></figcaption></figure>


# Using Advanced Conditionals

Using Advanced Conditionals in Looply Workflows

{% hint style="info" %}
New to conditionals? [Setting up advanced conditionals](https://academy.looply.ai/~/revisions/pEzhOhCQmzw0jAeIkFbI/workflows/using-advanced-conditionals/setting-up-advanced-conditionals)
{% endhint %}

The Advanced Conditional step in Looply allows users to build complex if & else logic within their workflows with multiple branches. This step enables users to create sophisticated conditional logic, ensuring that workflows can handle a wide range of scenarios and data conditions efficiently.

## **Overview**

The Advanced Conditional step is designed to offer more flexibility and power compared to the older Conditional and Branch Conditional steps. Here's how it works and how you can configure and use it:

1. **Multiple Branches**: Users can create multiple branches within an .Advanced Conditional step. Each branch can be specified as either an `IF` or `ELSE` branch, allowing for intricate logic flows.
2. **Complex Logic**: Within `IF` branches, users can build complex logic using `IF` and `AND`/`OR` conditions. This allows for detailed evaluation of data passing through the workflow from previous steps.
3. **Branch Reordering**: Users have the ability to reorder their `IF` statement branches, determining the sequence in which they are evaluated. This ensures that the most critical conditions are checked first.

<figure><img src="/files/6khpqHjxbCWSNpf2sAJ9" alt=""><figcaption><p>Advanced Conditional - Example</p></figcaption></figure>

## Define Conditional Logic

You can get started adding your conditional logic by clicking on a **+** button in your workflow and selecting the **Advanced** **Conditional** step from the Workflow's toolbox.  &#x20;

This will add the advanced conditional block to your workflow where you can begin building your logic branches. If you insert an advanced conditional step in-between 2 existing steps, the workflow chain will be broken to allow you to build out your conditional logic and can be manually re-connected after.&#x20;

### Adding Logic Branches

You can add logic branches to your Advanced Conditional step by **clicking and dragging** from the step on to the workflow builder canvas. This will bring up the **Create Condition** dialog where you can define your condition logic.&#x20;

Advanced Conditional branches are made up of the following components:&#x20;

* **Label:** a label or description for your branch which will only be visible to you within your workflow.
* **Condition Type:** specifies the type of condition for your branch (`IF` or `ELSE`) - please note you can only have 1 `ELSE` condition per Advanced Conditional step.&#x20;
* **Rules:** the conditional rule groups that contain your `IF` statement logic - only applicable to `IF` condition types.&#x20;

<figure><img src="/files/djKH6MspGXeQZRr9lmS9" alt=""><figcaption><p>Advanced Conditional - Create Condition Dialog</p></figcaption></figure>

#### Adding Rule Groups

Each `IF` condition logic branch can have multiple groups of rules, with each group being defined as `AND` / `OR` logic.&#x20;

* New rule groups can be added by clicking the **+ Add Group** button.&#x20;
* Use the `AND` / `OR` icon buttons to toggle between the rule group type.
* You can use the **trash can** icon button to delete the rule group

#### Adding Rules&#x20;

Each rule group can have multiple conditions inside, all of which must be true for the overall rule group to evaluate as `true`.&#x20;

Each rule consists of an **attribute**, a **condition** and a **value**. You can use these fields to evaluate data from within your workflow.&#x20;

New rules can be added to a group by clicking the **+ Add Rule** button.&#x20;

#### Pseudocode View

<figure><img src="/files/jGMcYNMU4dUM06XyjGyT" alt=""><figcaption><p>Advanced Conditional - Pseudocode View</p></figcaption></figure>

To help visualise your conditional logic, we've added an easy to understand pseudocode view which displays your conditions in plain english as you build them. &#x20;

### Re-Ordering Conditions

Once you have defined your logic branches for the Advanced Conditional step, you can use the Advanced Conditional Editor in the sidebar to specify the order your conditions should be evaluated.&#x20;

You can access the Editor by **clicking and selecting** the Advanced Conditional step from within the workflow builder canvas.&#x20;

<figure><img src="/files/serFZubaHEYru75SXQIb" alt=""><figcaption><p>Advanced Conditional - Re-Ordering Conditions</p></figcaption></figure>

You can re-order your conditions from within this editor by **clicking and dragging the drag icon** button (located on the left of the condition item) and moving the condition up or down within the list.&#x20;

Each branch defined here will be evaluated when your workflow from the top down.&#x20;

`ELSE` conditions **cannot be re-ordered** as they will always be executed last if no other conditions are met.&#x20;


# Setting up advanced conditionals

Advanced Conditional Logic

Learn how to build powerful conditional workflows using AND/OR operators in Looply.

***

### Introduction

Conditional logic allows your workflows to make smart decisions based on multiple criteria. This guide will teach you how to combine conditions using AND and OR operators to create sophisticated business rules.

By the end of this tutorial, you'll understand:

* How AND and OR operators work together
* The concept of condition groups
* How to build complex business logic in the UI
* Workarounds for advanced scenarios

***

### Understanding the Basics

#### The Golden Rule

> **OR creates groups. AND combines conditions within a group.**

Think of it this way:

| Operator | What it does                                        |
| -------- | --------------------------------------------------- |
| **OR**   | Starts a new possibility                            |
| **AND**  | Adds another requirement to the current possibility |

When your workflow evaluates conditions, it checks if **any one group** is fully satisfied. Within each group, **all conditions** must be true.

***

### How Condition Groups Work

#### Building Groups Step by Step

When you add conditions in the Looply UI, they're processed in order:

1. Start with the first condition (this begins Group 1)
2. Each **AND** condition gets added to the current group
3. Each **OR** condition starts a brand new group
4. Continue until all conditions are added

#### Visual Example

Consider these five conditions:

| # | Condition       | Type   | Group   |
| - | --------------- | ------ | ------- |
| 1 | status = 201    | SIMPLE | Group 1 |
| 2 | subrc = 0       | AND    | Group 1 |
| 3 | var1 > 10       | OR     | Group 2 |
| 4 | var2 = "active" | AND    | Group 2 |
| 5 | var3 = 100      | OR     | Group 3 |

This creates three groups:

* **Group 1:** status = 201 AND subrc = 0
* **Group 2:** var1 > 10 AND var2 = "active"
* **Group 3:** var3 = 100

**Final evaluation:** Group 1 OR Group 2 OR Group 3

The workflow proceeds if **any one** of these groups evaluates to true.

***

### Practical Example: Order Processing

Let's build a real-world scenario. Your business wants to process an order if:

* Status is "approved" AND amount is less than £1,000, **OR**
* Priority is "high" AND customer is "premium" AND region is "US", **OR**
* Override flag is enabled

#### Building This in the UI

| Step | Condition      | Operator  | Value      | Type   |
| ---- | -------------- | --------- | ---------- | ------ |
| 1    | status         | equals    | "approved" | SIMPLE |
| 2    | amount         | less than | 1000       | AND    |
| 3    | priority       | equals    | "high"     | OR     |
| 4    | customer\_type | equals    | "premium"  | AND    |
| 5    | region         | equals    | "US"       | AND    |
| 6    | override\_flag | equals    | true       | OR     |

#### Resulting Groups

| Group   | Logic                                                              |
| ------- | ------------------------------------------------------------------ |
| Group 1 | status = "approved" AND amount < 1000                              |
| Group 2 | priority = "high" AND customer\_type = "premium" AND region = "US" |
| Group 3 | override\_flag = true                                              |

**Final Logic:** Group 1 OR Group 2 OR Group 3

#### Test Scenarios

| Test Case          | status   | amount | priority | customer\_type | region | override | Result           |
| ------------------ | -------- | ------ | -------- | -------------- | ------ | -------- | ---------------- |
| Standard approval  | approved | 500    | normal   | standard       | UK     | false    | ✅ Pass (Group 1) |
| Premium fast-track | pending  | 5000   | high     | premium        | US     | false    | ✅ Pass (Group 2) |
| Manager override   | rejected | 10000  | low      | standard       | UK     | true     | ✅ Pass (Group 3) |
| No match           | pending  | 2000   | normal   | standard       | UK     | false    | ❌ Fail           |

***

### Advanced Scenarios

#### What's Not Supported

The current system uses flat operator precedence where AND binds tighter than OR. This covers the vast majority of workflow use cases, but **nested parentheses are not directly supported**.

Examples that can't be built directly:

* `A AND (B OR C)` — nested OR within AND
* `(A OR B) AND (C OR D)` — multiple nested groups

#### The Solution: Distributive Law

You can rewrite nested expressions using mathematical equivalence. This is the same principle from Boolean algebra.

**Example 1: A AND (B OR C)**

**Equivalent form:** (A AND B) OR (A AND C)

Build it like this:

| Step | Condition | Value    | Type   |
| ---- | --------- | -------- | ------ |
| 1    | A         | value\_a | SIMPLE |
| 2    | B         | value\_b | AND    |
| 3    | A         | value\_a | OR     |
| 4    | C         | value\_c | AND    |

**Result:** Two groups that together equal the original nested logic.

**Example 2: (A OR B) AND (C OR D)**

**Equivalent form:** (A AND C) OR (A AND D) OR (B AND C) OR (B AND D)

Build it like this:

| Step | Condition | Value    | Type   |
| ---- | --------- | -------- | ------ |
| 1    | A         | value\_a | SIMPLE |
| 2    | C         | value\_c | AND    |
| 3    | A         | value\_a | OR     |
| 4    | D         | value\_d | AND    |
| 5    | B         | value\_b | OR     |
| 6    | C         | value\_c | AND    |
| 7    | B         | value\_b | OR     |
| 8    | D         | value\_d | AND    |

**Result:** Four groups that together equal the original nested logic.

{% hint style="info" %} **Pro tip:** While this expansion creates more conditions, the logic is mathematically identical. Your workflow will behave exactly as intended. {% endhint %}

***

### Quick Reference

#### Operator Behaviour Summary

| Operator | Creates New Group?    | Effect                |
| -------- | --------------------- | --------------------- |
| SIMPLE   | Yes (first condition) | Starts Group 1        |
| AND      | No                    | Adds to current group |
| OR       | Yes                   | Starts new group      |

#### Evaluation Rules

1. All conditions in a group must be TRUE for the group to pass
2. Only ONE group needs to pass for the overall condition to succeed
3. Groups are evaluated independently

#### Common Patterns

| Business Need               | Pattern                                |
| --------------------------- | -------------------------------------- |
| All criteria must match     | Use only AND operators                 |
| Any one criterion is enough | Use only OR operators                  |
| Multiple valid paths        | Mix AND within groups, OR between them |
| Fallback conditions         | Put fallback as final OR group         |

***

### Summary

* **OR** separates your conditions into independent groups
* **AND** chains conditions together within a group
* The workflow passes if **any single group** is fully satisfied
* For nested logic, use the distributive law to expand into equivalent flat conditions


# Using Integrations

Using your integrations in Looply workflows

Integrations in Looply open the door to a new dimension of efficiency by allowing users to weave external services into their workflows. This means your workflows can now tap into the unique capabilities of integrated services, turning routine tasks into streamlined processes. In this documentation, we'll guide you through the steps of accessing and utilizing these integrations within your Looply workflows, empowering you to achieve more with every step.

Integration actions we currently support:&#x20;

* [Microsoft - Adaptive Card Actions](/workflows/using-integrations/adaptive-card-actions)
* [SAP - HTTP Requests](/workflows/using-integrations/sap-requests)

#### Adding Integrations

You can locate supported integration actions below the **Integrations** section of the Workflow Toolbox.&#x20;

<figure><img src="/files/m6VZRZ0cJ3OKlo04DaMD" alt=""><figcaption></figcaption></figure>

Add any of our integration actions to your workflow by simply clicking on the action.&#x20;


# Adaptive Card Actions

Performing adaptive card related actions in your Looply workflows

Adaptive Card Actions provide a direct avenue for users to harness the full potential of their Microsoft integration within workflows.&#x20;

Specifically designed for efficiency, these steps empower users to send, update, or delete Adaptive Cards seamlessly using their integrated Microsoft account and Looply-created apps.&#x20;

Imagine the ability to notify users of events within your system or process by incorporating Adaptive Card Actions into your workflow. It's a precise, no-nonsense approach to integrating Adaptive Cards that aligns seamlessly with your communication and notification needs.

## Configuring Actions

You can get started configuring your adaptive card action by clicking on a **+** button in your workflow and selecting the **Adaptive Card Action** step from the Workflow's toolbox.  &#x20;

<figure><img src="/files/bQlShUUZNsdpoWRmNKP8" alt=""><figcaption></figcaption></figure>

### Sending Adaptive Cards

You can instruct your workflow to send an adaptive card by using the **Card Action** dropdown menu and selecting **Send**.&#x20;

<figure><img src="/files/mgZkRMlC5Fr1BzTUUjzL" alt=""><figcaption></figcaption></figure>

#### App Selection

For every adaptive card action you wish to carry out with Looply, you'll need to define the Microsoft Teams app you want to use to process the action.&#x20;

Use the **Select App** dropdown to select which Looply-created Microsoft Teams app you wish to use to dispatch your adaptive card.&#x20;

#### Card Selection

You can use the **Select New Card** dropdown menu to select the Adaptive Card you wish to send to the user.&#x20;

Additionally, if you haven't created any adaptive cards yet or wish to create a new adaptive card to be used - you can select '**create a new adaptive card**' from the dropdown menu. Once selected, a new adaptive card instance will be created and automatically inserted to be used within your adaptive card action.&#x20;

See [Building Adaptive Cards](/adaptive-cards/building-adaptive-cards) to learn more about creating or editing adaptive cards.&#x20;

#### Recipients

Use the **Recipients** input field to determine which users should receive the adaptive card.&#x20;

You can choose to enter your recipients manually or use the dropdown to access data from your event trigger or existing steps.&#x20;

{% hint style="info" %}
**Tip:** When manually entering a recipient, ensure accuracy by using their Microsoft username. This guarantees a seamless connection between Looply and your Microsoft account, ensuring the intended recipient receives the notification promptly.\
\
For example: `USERNAME@x3qlj.onmicrosoft.com`
{% endhint %}

### Updating Adaptive Cards

You can instruct your workflow to update an adaptive card that was previously dispatched by using the **Card Action** dropdown menu and selecting **Update**.&#x20;

Updating adaptive cards can be useful for displaying new information to your user, for example, if the status of their notification has changed.&#x20;

#### Select Previous Card

To update an adaptive card, you'll first need to select the existing card that was dispatched and should now be updated.&#x20;

Use the **Select Existing Card** dropdown menu to select the previous card from your workflow that you want to update.&#x20;

#### Select New Card

Use the **Select New Card** dropdown menu to select the new adaptive card that you wish to replace the previous one.&#x20;

### Deleting Adaptive Cards

Additionally, you can use Adaptive Card Action steps to delete previously dispatched adaptive cards to prevent the recipient from being able to view or access them.&#x20;

This can be useful if you want your notification to expire or no longer require it for a user.&#x20;

You can instruct your workflow to delete an adaptive card that was previously dispatched by using the **Card Action** dropdown menu and selecting **Delete**.&#x20;

#### Select Previous Card

Use the **Select Existing Card** dropdown menu to select the previous card from your workflow that you want to delete.&#x20;

### Notifying Users

When updating or deleting an adaptive card which has been dispatched to a user, you can configure the action to notify the user by toggling the **Notify User** switch from the configuration options.&#x20;

This can be useful when you want the recipient to be made aware of the update or deletion of the adaptive card they previously received.&#x20;

By using the notify user setting, when an update action is processed - the existing card will be deleted and a new one will be dispatched which will ensure the updated notification is pushed to the front of their Teams chat messages. Similarly, when a delete action is processed - a new notification will be issued to the user informing them that the previous message has been deleted.&#x20;

## Handling Responses

You can instruct your Looply workflow to accept and handle a response from a user when sending adaptive cards. This is often used in workflows where the recipient is expected to respond to the adaptive card using buttons located on the card. For example, approving or rejecting an event.&#x20;

#### Allowing Responses

When using Adaptive Card Action steps to send a new card to a user, you can add support for accepting a response by toggling the **Allow Responses (Teams)** or **Allow Responses (External)** switch.&#x20;

When you allow responses, the Adaptive Card Action step within your workflow will immediately insert a new **Card Response Handler** block, followed by separate branches to handle a response from Microsoft Teams (directly from the adaptive card) or an external response.&#x20;

### Card Response Handler

Card Response Handler blocks are inserted in your workflow when you choose to allow responses from your Adaptive Card send action.&#x20;

These handlers operate by keeping your workflow in a processing state while they wait for a response to be received. Currently, our Card Response Handlers support responses directly from the adaptive card or alternatively an external response from outside your adaptive card.&#x20;

<figure><img src="/files/gRhm8HAZeUcjNtS8RFuM" alt=""><figcaption></figcaption></figure>

### MS Teams Response

The **MS Teams Response** branch can be used to handle a direct response from Microsoft Teams via your adaptive card. This is most commonly used to process button clicks for specific actions on your adaptive card.&#x20;

To begin creating your response, click on the placeholder step within the **MS Teams Response** branch and then select the workflow step you wish to insert here from the Toolbox.&#x20;

<figure><img src="/files/pFbkqYdmI2WeCB3J0dCb" alt=""><figcaption></figcaption></figure>

### External Response

Alternatively, you might want to handle a response or trigger from outside Microsoft Teams relating to the current process. This can be useful if your process has been updated from within a different system or workflow, and you need to update or delete the adaptive card.&#x20;

To handle an external response within your workflow, click on the placeholder step within the **External Response** branch and then select the workflow step you wish to insert here from the Toolbox.&#x20;

## Authenticating Actions

Adaptive Card Actions can be authenticated through a configured Looply integration destination that supports an end-user authentication method such as OAuth2.0 or Basic Authentication.&#x20;

### Adding Authentication

To add authentication to your adaptive card action, navigate to the **Advanced** tab within the Adaptive Card Action toolbox editor.&#x20;

From here, you can open the **Authentication** section and select the **Require Authentication** option.&#x20;

This will make the integration selection dropdown menu available where you can view a list of all available integrations that have been configured which support end-user authentication for this step. Simply, select the integration you want to use to authenticate your action.&#x20;

Now, when you execute your workflow and your adaptive card is sent - when the user attempts to execute an action on the card via action buttons they will be prompted to authenticate via the integration destination credentials supplied.&#x20;

### Using Authentication Response

Authentication response data is made available to any subsequent workflow steps from the Card Response Handler.&#x20;

This includes data such as bearer tokens and any other metadata returned from the authentication process.&#x20;

This data can then be used to authenticate subsequent steps, such as HTTP Requests to the same integration client API.&#x20;


# SAP Requests

Sending HTTP Requests to your integrated SAP system with Looply workflows

Seamlessly integrate SAP Requests within your workflows, establishing a direct communication channel with your configured SAP systems.&#x20;

Here, the power of HTTP Requests becomes your strategic tool, enabling seamless updates within your SAP environment triggered by specific workflow events. This documentation guides you through the precise steps, ensuring you unlock the full potential of SAP Requests for a synchronized and efficient workflow experience.

## Configuring Requests

You can get started configuring your adaptive card action by clicking on a **+** button in your workflow and selecting the **SAP Request** step from the Workflow's toolbox.  &#x20;

SAP Requests sent from Looply support the following configuration options:&#x20;

* SAP Profile or System ID & Client ID
* Service Path
* Entity Name
* Request Type
  * `GET`
  * `POST`
  * `PUT`
  * `PATCH`
  * `DELETE`
* URL Parameters
* Headers
* Body Type
  * `JSON`
  * `multipart/form-data`
  * `x-www-form-urlencoded`
  * `XML`
  * `Raw`
* Body

### SAP System

Take control of your HTTP Requests by specifying the destination SAP system. This crucial step ensures that your request reaches the intended target, enabling precise and effective communication with your configured SAP environment.

This can be done through pre-configured SAP Profiles or by manually entering system credentials.&#x20;

#### Profiles

Use the **SAP Profile** dropdown menu to view and select any of your pre-configured SAP Profiles that you have created and integrated with Looply.&#x20;

See [SAP Integration](/integrations/sap-integration) for more information on creating SAP Profiles.&#x20;

#### Manual System Credentials

Alternatively, you can choose to manually enter a system ID and client ID for your SAP system by toggling the **Enter manual system credentials** switch.&#x20;

This can be useful if you wish to communicate with multiple different SAP systems and determine system credentials at runtime with data binding.&#x20;

For example, you might wish to specify your system ID in your workflow payload instead and bind it here.&#x20;

To enter or bind manual system credentials you can use the **System ID** and **Client ID** input fields which will become available once the switch has been toggled. Data from your workflow payload or previous steps can be accessed in these fields using the dropdown menu within.&#x20;

{% hint style="info" %}
**Note:** You must configure an SAP Profile for every system you attempt to communicate with, even if using manual system credentials. See [SAP Integration](/integrations/sap-integration) for more information on creating SAP Profiles.&#x20;
{% endhint %}

#### Service Path

Use the **Service Path** input field to enter the path of the specific service within the system you are requesting.&#x20;

#### Entity Name

Use the **Entity Name** input field to enter the entity name you wish to target.&#x20;

### Request

You can specify the type of request to be sent by selecting the desired **request method** from the dropdown menu. Choose from `GET`, `POST`, `PUT`, `PATCH`, or `DELETE`. Then, in the **Request URL** text field, enter the accurate destination URL to which you want to send the request. Make sure the URL is correctly formatted to ensure successful communication.

#### URL Parameters

Users are also able to add query string parameters in their request if they require them under the **URL Parameters** section.&#x20;

Add specific query string parameters by entering their names and values in the corresponding **Key** and **Value** inputs. Need more parameters? Simply click **+** **Add parameter** for additional sets of inputs. This flexibility empowers you to tailor your request to meet specific criteria.

#### Headers

Additionally, users can specify specific headers they wish to attach to their request under the **Headers** section.&#x20;

Add headers by entering their names and values in the corresponding **Key** and **Value** inputs. Click the **+** **Add header** button for inserting more than one header.&#x20;

### Payload

The **Body** section allows you to define the payload of your HTTP request. Select the appropriate 'Body Type' from the dropdown menu - `JSON`, `multipart/form-data`, `x-www-urlencoded`, `XML`, or `Raw`.&#x20;

For `JSON`, `multipart/form-data`, and `x-www-urlencoded`, use the **key-value pair inputs** to finely tune your payload - using the **+ Add item** button to insert more than one data item.&#x20;

Alternatively, for `XML` or `Raw` data, leverage the **text area** to input your content directly.&#x20;

### Failure Scenarios

Navigate through potential hiccups effortlessly - Looply equips you with options to gracefully handle failures in your SAP Requests, offering the ability to continue on failure and automatic retry functionalities.

#### Continue on Failure

Looply supports the ability to continue executing your workflow in the event the SAP Request step fails. This can be useful if you wish to manually handle any errors or if your workflow is not reliant on the response from the request.&#x20;

This functionality can be enabled by toggling the **Continue if request fails** switch within the SAP Request editor.&#x20;

#### Automatic Retry

Additionally, users can enable automatic retries for their SAP Request where in the event of request failure - Looply will automatically attempt the request again. This feature is currently limited to a maximum of 100 attempts before the step will fail.&#x20;

To enable automatic retries, toggle the **Auto-retry** switch within the SAP Request editor.&#x20;

## Testing Requests

SAP Requests can be effortlessly validated by configuring your request data and using the Test Step feature.&#x20;

Simply click the **Test Step** button in the header of the SAP Request editor, execute your request, and receive instant feedback.&#x20;

The response status and any returned output will be displayed, ensuring a seamless testing experience within the confines of your workflow.


# Using Redirects

Redirect between steps in your Looply Workflows

Redirect Steps enable users to navigate within a workflow either backwards, to create a loop and repeat a series of events - or forwards, to skip over a series of steps.

## Configure Redirects

You can get started configuring your redirect by clicking on a **+** button in your workflow and selecting the **Redirect** step from the Workflow's toolbox. &#x20;

Afterward, open the **dropdown menu** in the step sidebar and choose the specific step you wish to redirect to from this point in your workflow.

#### Redirect Limitations

The following steps are **not supported** when using redirects within your workflow, and therefore cannot be selected from the step selection dropdown menu.&#x20;

* Event Trigger - prevents re-execution of entire workflow (use Trigger Workflow step instead)
* Redirect - prevents possible infinite loops or chained redirects
* Terminate - prevents redirect to termination (use direct Terminate step instead)


# Using Override Payload

In the intricate ecosystem of workflow automation, the agility to adapt and modify data as it flows from one step to another is a cornerstone of dynamic process management

The **Override Payload** step offers users the ability to either replace or update the current workflow payload with new or modified data. This capability ensures that subsequent steps in the workflow can operate on the most current and relevant data, enabling a level of customisation and responsiveness that is critical in today’s fast-paced environments.

## Configuring Override Payload

You can get started configuring an override payload step by clicking on a **+** button in your workflow and selecting the **Override Payload** step from the Workflow's toolbox. &#x20;

This step will automatically pull in your most up to date payload data from your workflow schema and auto-complete the payload editor with it.&#x20;

### Using Parameters

To make changing your payload data easier, you can configure a set of parameters that can hold either a static value or dynamic value bound from your workflow steps.&#x20;

Configure your parameters by adding a name and value for each parameter, tailoring the data to your specific needs. Need more parameters? No problem. Click the **+** **Add parameter** button to add more.

Users can decide to manually enter static values for each parameter, or use the dropdown menu to select a value from a previous step in your workflow.&#x20;

### Payload Editor

To help you get started, the  JSON payload editor will be loaded automatically with your most up to date payload data pre-filled.&#x20;

If your payload data changes while you're working on this step, you can update the editor with the latest changes by simply clicking the **Restore** button. &#x20;

{% hint style="info" %}
**Note:** Clicking **Restore** within the payload editor will pull in your latest payload data from your workflow schema - but any changes you have made within the payload editor may be lost.&#x20;
{% endhint %}

You can access the JSON payload editor to add new values or simply update the existing ones. Values can be assigned by entering them manually or referencing any previously configured parameters.&#x20;

Parameters can be accessed in your editor by referencing the parameter in the following format: `${parameter_name}`

### Testing Changes

You can test your updated payload at any time by clicking the **Test Step** button located in the header of the Override Payload sidebar.&#x20;

The output of your new payload will be returned and displayed reflecting any new attributes, values or bound data.&#x20;

{% hint style="danger" %}
**Note:** Override Payload is listed as an Advanced step within Looply Workflows. Updating or removing payload values of significant importance within your workflow steps may cause breaking changes.&#x20;
{% endhint %}


# Terminating Workflows

In the dynamic environment of workflow automation, controlling when a workflow ends is just as crucial as managing its start.

The **Terminate Step** is a powerful tool designed to explicitly define a point within your workflow where the execution should immediately cease. Whether to halt a process due to an error, fulfil a specific condition, or simply end a workflow after completing its intended task, the Terminate Step provides a straightforward and effective solution.

## Adding Terminate Steps

You can get started adding a terminate step by clicking on a **+** button in your workflow and selecting the **Terminate** step from the Workflow's toolbox. &#x20;

No further configuration is required - execution will be halted immediately when this step is reached within your workflow. You can add as many terminates as you require to handle different flows within your process.

{% hint style="info" %}
Terminate Steps are append only steps - which means they cannot be inserted in between 2 steps as this would break your workflow chain.&#x20;
{% endhint %}


# Variable Datastores

A central store for reusable JSON data across your Looply workflows

Looply's **Variable Datastore** is a flexible storage solution designed to help you manage and reuse custom JSON data across your workflows. Whether you're working with large datasets or simply need a central place to store structured information, variable datastores make it easy to create, access, and maintain object-based data within the Looply platform. By storing your JSON data in a datastore, you can reference it in multiple workflows, reduce duplication, and keep your logic clean and maintainable.

<figure><img src="/files/JXeOPAPWe75umdqr6d5a" alt=""><figcaption></figcaption></figure>

## Creating Datastores

You can get started creating datastores by navigating to the Looply Variable Datastore tool and clicking **+ Create Datastore**.&#x20;

<figure><img src="/files/s2iqsfyJkglYszNdaChP" alt=""><figcaption></figcaption></figure>

Once the Create Datastore dialog opens, simply give your datastore a unique name which can be used to identify your storage object and click **Create**.&#x20;

## Editing Data

Once you've created a Variable Datastore in Looply, you can easily add, view, and modify its content directly through the **Datastore Editor** interface.

<figure><img src="/files/RYQqwfs6NFucHEkaGkYr" alt=""><figcaption></figcaption></figure>

### Editing JSON Data

On the right-hand side of the editor, you'll see a formatted JSON editor where you can directly input or update your datastore contents. This is where you define your key structure and values—such as arrays, objects, strings, or numbers—based on how your workflows need to use the data.

For example, in a datastore like `LineManagerData`, you might store an array of line manager objects, each with fields like `id`, `name`, `email`, `department`, and a list of `reports`.

The JSON editor supports syntax highlighting and formatting, making it easy to edit structured data at scale. Use the **Format** button at the top to automatically tidy up your structure, and **Save** your changes once you're ready.

### Browsing and Editing Properties

On the left-hand side, the **Properties** panel provides a more visual breakdown of the current datastore structure. Each nested object and value is displayed in a readable format, allowing you to quickly inspect or navigate your data—especially useful for larger or deeply nested datasets.

### Datastore Settings

<figure><img src="/files/Q1fBlvDq88wqBevi7Fhn" alt=""><figcaption></figcaption></figure>

Click the **Datastore Settings** tab to view metadata such as:

* Datastore name
* File size
* Created and last modified dates
* (Coming Soon) Encryption toggle for securing sensitive data

While encryption is not currently available, this setting will soon allow you to protect your datastore content with a simple toggle.

## Deleting a Datastore

{% hint style="warning" %}
**Note:** This action cannot be undone. Be sure to back up any important data before deleting.
{% endhint %}

If you no longer need a datastore, you can permanently delete it from the **Datastore Settings** tab. This action is irreversible, so proceed with caution.

<figure><img src="/files/qhRjjvtGUbZ6lORwQmft" alt=""><figcaption></figcaption></figure>

To delete a datastore:

1. Navigate to the **Datastore Settings** tab of the datastore you wish to remove.
2. Click the **Delete datastore** button.
3. A confirmation dialog will appear, asking you to manually type the name of the datastore (e.g. `LineManagerData`) to confirm the deletion.
4. Once the correct name is entered, the **Delete datastore** button will become active.
5. Click it to permanently remove the datastore and all of its content.

## Using Datastores in Workflows

Variable Datastores can be easily integrated into your workflows to provide reusable data across steps—ideal for things like configuration values, reference mappings, or shared resources like `LineManagerData`.

### Adding a Datastore to a Workflow

<figure><img src="/files/Ace6sFgrN89RwtQP0kXK" alt=""><figcaption></figcaption></figure>

To use a datastore in a workflow:

1. Open your workflow in the **Workflow Studio**.
2. Click the **overflow menu (⋮)** at the top right of the workflow canvas.
3. Select **Datastores** from the dropdown.
4. From the **Active Datastores** popup, select one or more datastores you want to use in this workflow (e.g., `LineManagerData`).
5. Click **Save** to confirm your selection.

The selected datastores will now be bound to the current workflow and available for use within each step.

### Accessing Datastore Data in Steps

Once a datastore is attached to a workflow, its contents are automatically available in the **data binding menu** of each workflow step.

<figure><img src="/files/WSs7W33xKw0eBgkZtbc0" alt=""><figcaption></figcaption></figure>

When configuring a step (such as a function, condition, or integration call), click into the parameters or input fields and select values from the datastore—these will appear in the selection menu with their names and structure clearly outlined. For example:

* `datastore_id` → the unique ID of the datastore
* `line_managers` → the full array of line manager objects

This allows you to pass datastore data into functions, reference it in conditional logic, or enrich outbound payloads using structured and centralised data.


# Monitoring Workflows

Workflow Monitoring and Management Made Simple

**Looply provides powerful tracking and monitoring for all your workflows.**\
Instantly view the real-time status of your workflows — whether they are in progress, completed, awaiting response, or failed — and gain detailed insights into each step of the execution.

## Workflow History

The Workflow History page provides a full overview of all your executed Looply Workflows — whether they are in-progress, completed, awaiting response, or failed.

<figure><img src="/files/9mhjGjjBA4E4eiwCNZOL" alt=""><figcaption></figcaption></figure>

Each workflow execution is listed with the following information:

* **Date and time** the execution was triggered
* **Process ID** (unique identifier for the execution or external process)
* **Workflow ID**
* **Workflow Name**
* **Version Number** of the Workflow
* **Trigger Type** (e.g., HTTP request, Scheduled Workflow)
* **Current Status** (such as In Progress, Awaiting Response, Completed, or Failed)
* **Warnings**

### Search and Filtering

Finding specific workflow executions is easy with Looply’s powerful search and filtering options.

#### Global Search

Use the global search bar to search across:

* **Process ID**
* **Workflow ID**
* **Workflow Name**

<figure><img src="/files/LJz2xKkTMiwg4weYnIbH" alt=""><figcaption></figcaption></figure>

You can add **one or more search terms** to refine your results:

* Enter a search term and either **click the search icon** or **press Enter** to add it.
* Search terms will appear as **tags** above the Workflow History table.
* Remove a search term at any time by clicking the **X icon** beside its tag.
* **Wildcard searching** is supported by using the `*` character within a search term (e.g., `HOL*` will match anything beginning with `HOL`).

When multiple search terms are entered, any record that matches **any of the search terms** will be returned.

#### Filters

<figure><img src="/files/o8llJXl2C0Qws6GdWAOJ" alt=""><figcaption></figcaption></figure>

Further refine your search results using filters:

* **Status**: Filter workflow executions by one or more execution statuses (e.g., Failed, Awaiting Response).
* **Sort Order**: Sort executions by **Newest First** or **Oldest First**.
* **Date Range**: Apply a custom date range to show executions triggered within specific dates.

#### Pagination

Effortlessly browse through your filtered results using enhanced pagination controls, ensuring smooth navigation across multiple pages of execution data.

## Examining Executions

To view detailed information about a specific workflow execution, navigate to the Workflow History table and **click on the Process ID** of the execution you want to inspect.\
This will open the **Workflow Execution Detail Page**, a fully interactive screen that displays the execution's full journey step-by-step.

<figure><img src="/files/EcsHQ2xIpni02ClI2q7Z" alt=""><figcaption></figcaption></figure>

Within this view, each workflow step is visualized, allowing you to:

* See the overall flow and structure of the execution.
* Click on any workflow step to review its inputs, outputs, timing, and success status.

### Step Details

Selecting a workflow step within the execution provides an in-depth view of:

* **Input parameters** configured for the step.
* **Start and completion timestamps** for the step.
* **Total duration** the step took to execute.
* **Current status** of the step (Success, Failed, Awaiting Response, etc.).

Refer to the [Step Status Table](#step-statuses) for definitions of all possible step statuses.

### Step Input JSON

Each step within a workflow requires specific inputs to function correctly.\
Inputs can be:

* **Manually entered** during workflow design, or
* **Bound dynamically** from the payload or outputs of previous steps.

When examining a step during execution, Looply displays the **Input JSON**, which includes:

* **Current Step Inputs**: All fields specific to the selected step.
* **Previous Step Outputs**: Nested under the `$` attribute.

**Example Input JSON (Dispatch Adaptive Card Step):**

```json
{
  "allow_responses": true,
  "card_version": 1,
  "previous_card_version": 0,
  "accessible_nodes": ["****"],
  "card_id": "****",
  "previous_card_id": "",
  "card_name": "Card Name 1",
  "name": "****",
  "recipient": "",
  "action": "SEND",
  "state_id": "adaptiveCard_1",
  "app_id": "****",
  "value": "",
  "$": {
    "payload": {
      "description": "Request",
      "output": {}
    },
    "function_3": {
      "description": "Time Formatter Function",
      "output": "2023-10-18"
    }
  }
}
```

🔹 The **top level** contains the current steps's specific inputs.\
🔹 The **`$` attribute** contains the accumulated outputs from all previous steps.

{% hint style="info" %}
**Note:** The `payload` section inside `$` contains the original data passed to the workflow, visible primarily in workflows triggered via HTTP requests.
{% endhint %}

### Step Output JSON

The Output JSON shows the data generated by the currently selected step.

**Example Output JSON (Function Step):**

```json
{
  "payload": {
    "description": "Request",
    "output": {}
  },
  "function_3": {
    "description": "Time Formatter Function",
    "output": "2023-10-18"
  }
}
```

🔹 Unlike Input JSON, **previous step outputs are listed at the top level** (not nested under `$`).\
🔹 However, this output will become part of the `$` attribute in the Input JSON for subsequent steps.

### Error Logging

If a step fails due to incorrect configuration or unexpected issues, Looply provides detailed error outputs.\
These include the **error type**, **error message**, and a **stack trace**.

**Example Error Output (Dispatch Adaptive Card Step):**

```json
{
  "errorType": "Error",
  "errorMessage": "No user found",
  "trace": [
    "Error: No user found",
    "    at Runtime.dispatchAdaptiveCard [as handler] (/var/task/src/handlers/step-function/dispatchAdaptiveCard.js:183262:13)",
    "    at processTicksAndRejections (node:internal/process/task_queues:96:5)"
  ]
}
```

🔹 Errors are displayed directly within the step details, enabling you to quickly diagnose and correct issues.

**Want to stay instantly informed when a workflow fails?**\
Learn how to configure [**Error Notifications** ](/monitoring-and-logs/error-notifications)to automatically alert you when issues occur.

### Step Statuses

| Status             | Description                                                                                                 | Status Colour |
| ------------------ | ----------------------------------------------------------------------------------------------------------- | ------------- |
| SUCCESS            | Step successfully completed without any errors and has passed its result onto the next step                 | Green         |
| FAILED             | Step has errored out. Error will be shown against the steps Output JSON                                     | Red           |
| TERMINATED         | Step execution has been halted and will not continue                                                        | Red           |
| IN\_PROGRESS       | This step is in progress and is yet to complete                                                             | Orange        |
| AWAITING\_RESPONSE | The Looply Workflow has stopped and is waiting from a response from external resume or Adaptive Card Action | Orange        |

### Terminating Executions

Looply gives users full control over their workflows, including the ability to **terminate executions** that are either:

* In Progress, or
* Awaiting Response.

Terminating an execution is useful when:

* A process becomes irrelevant.
* An urgent correction is needed.
* An error requires immediate stopping.

<figure><img src="/files/pVslLE4SmpwFTqHtbZWF" alt=""><figcaption></figcaption></figure>

To terminate a workflow:

1. Open the **Workflow Execution Detail Page** for the execution.
2. Select **Terminate**.
3. The workflow will halt immediately, and no further steps will execute.

This ensures you maintain control, protect operational integrity, and avoid letting incomplete or erroneous workflows continue.


# Error Notifications

Configure email or webhook notifications if your workflow fails in Looply

In modern workflow automation, ensuring that each step of a process is executed correctly is crucial. However, errors can sometimes occur, and timely notifications are essential to promptly address these issues and maintain workflow integrity. Looply provides robust error notification features that allow users to configure alerts to be sent via email or to a specified webhook whenever a step in their workflow fails. This ensures that users are immediately informed of any issues, enabling quick troubleshooting and minimal disruption to their automated processes.

## Configuring Error Notifications

Looply offers flexible and customisable options for error notifications, tailored to meet the unique needs of each version of your workflows. You can choose to receive notifications directly to your email inbox or have them sent to a webhook, which can be integrated with your existing monitoring and alerting systems.

<figure><img src="/files/4gGQO9sWQbPqXE3ogrxU" alt=""><figcaption><p>Workflow - Error Notifications</p></figcaption></figure>

### Email Notifications

Email notifications in Looply provide a straightforward way to stay informed about errors in your workflows. You can configure email alerts to be sent every time an error occurs, ensuring immediate awareness and quick response to any issues. Notifications can be sent to one or multiple recipients of your choosing, allowing you to keep all relevant team members informed.

To prevent email spam and manage the frequency of notifications, Looply also allows you to set a preferred frequency for receiving error alerts. You can choose to receive notifications every hour, day, week, or month, depending on your needs. This flexibility ensures that you stay informed without being overwhelmed by too many emails.

Email notifications can be configured by entering a **Frequency** and one or more **Recipients** from within the Workflow Settings menu of your selected workflow version. &#x20;

### Webhook Notifications

Webhook notifications in Looply offer a powerful and flexible way to integrate error alerts with your existing systems. You can configure webhook notifications alongside email notifications, enabling you to use both methods concurrently. This ensures you have multiple channels of communication, enhancing your ability to respond promptly to any workflow errors.

By setting up a webhook URL, Looply will send a POST request with detailed error information to the specified endpoint every time a step fails. This allows you to send error notifications directly to your own system, where you can handle them appropriately based on your unique requirements.

Looply provides the following configuration options for webhook notifications:

* **Webhook URL**: Enter the URL to which Looply will send the error notifications.
* **Custom Headers**: Specify any custom headers that need to be included in the notification request - such as an authorisation token or API key.
* **Basic Authentication**: If accessing an authenticated URL, you can enter a username and password to enable basic authentication.

Once a webhook URL has been configured, Looply will automatically begin attempting to send POST requests with any information whenever an error occurs within your workflow.&#x20;

#### Example Webhook Notification Payload

```json
{
    "workflow_id": "02b2de0a-9a01-4e08-9844-4d9b8e3b37bc",
    "workflow_execution_id": "f39462a9-8b42-4d60-8a74-ca75b0a482cb",
    "organization_id": "2c3662ac-4367-547c-d1be-9f48fba22e63",
    "error": {
        "step": "My Function", 
        "name": "ReferenceError", 
        "message": "x is not defined"
    }
}
```

### Conditional Notifications

Error notifications can be enabled to be sent out conditionally based on workflow variables which can give you fine-grained control over your notification strategy, ensuring that your team only receives alerts when specific conditions are met.

#### How It Works

When enabled, error notifications (both email and webhook) will only be sent if the specified workflow variable evaluates to a truthy value. This allows you to implement custom logic to determine when notifications should be triggered.

#### Configuration

1. In the workflow settings panel, navigate to the **Error Notifications** section
2. Toggle the **Enable conditional notifications** switch to activate this feature
3. In the **Condition** field, specify a workflow variable that will control notification delivery
   * The variable must evaluate to a truthy value for notifications to be sent
   * If the variable is falsy (false, 0, null, undefined, empty string), no notifications will be sent

We support condition variables being selected from your incoming workflow payload, any imported datastores or your workflow environment variables.&#x20;


# Developer API Overview

Integrate Looply actions directly into your own applications

## Introduction

Explore Looply developer APIs - powerful tools that empower developers to seamlessly interact with Looply's functionality.&#x20;

{% content-ref url="/pages/s4gM4N2pksrevybhfCC6" %}
[Workflow API](/api-reference/workflow-api)
{% endcontent-ref %}

## API Keys

Developers can view existing API keys or generate new ones directly from the Looply dashboard **Developer Tools** section. &#x20;

Dive into a refined space where precision meets access control, ensuring your development team has the credentials needed for a seamless integration experience.

<figure><img src="/files/V4fQz6GoipyeAs5rZPMR" alt=""><figcaption></figcaption></figure>

#### Generating API Keys

Get started by generating a new API key for your team directly from the **API Keys** page within Looply.&#x20;

Create a new API key by clicking the **Create API Key +** button and providing a name for your key.&#x20;

#### Deleting API Keys

API Keys can be seamlessly removed by clicking on the **trash can** icon located next to the API Key.&#x20;


# Workflow API

All Looply Workflow related API endpoints

## triggerWorkflow

<mark style="color:green;">`POST`</mark>`https://api.looply.io/v2/workflows/triggerworkflow/{workflow_id}/{workflow_version}`

The `triggerWorkflow` endpoint initiates the execution of a specified workflow. When called, this endpoint triggers the workflow process, allowing you to automate various tasks and operations defined within the workflow. This endpoint can receive a JSON body payload to be passed to the workflow execution.  A `looply_execution_id` will be returned on successful trigger of the workflow.

> AWS IP address for whitelisting Looply API calls: **52.208.220.68**

#### Path Parameters

| Name                                           | Type   | Description             |
| ---------------------------------------------- | ------ | ----------------------- |
| workflow\_id<mark style="color:red;">\*</mark> | string | id of the workflow      |
| workflow\_version                              | string | version of the workflow |

#### Query String Parameters

| Name    | Type   | Description                                                                                                                  |
| ------- | ------ | ---------------------------------------------------------------------------------------------------------------------------- |
| profile | string | name of environment variable profile (see [Environment Variables & Profiles](/workflows/environment-variables-and-profiles)) |

#### Headers

| Name                                        | Type   | Description    |
| ------------------------------------------- | ------ | -------------- |
| x-api-key<mark style="color:red;">\*</mark> | string | Looply API Key |

{% tabs %}
{% tab title="200: OK Success Response" %}

```json
{
	"message": "success",
	"looply_execution_id": "****",
	// Optional: only included in response when a profile is specified
	"profile": "prod"
}
```

{% endtab %}

{% tab title="403: Forbidden Incorrect or No API Key Supplied" %}

{% endtab %}

{% tab title="500: Internal Server Error" %}
Serverside issue with this endpoint

```json
{
    "message": "something went wrong"
}
```

{% endtab %}

{% tab title="400: Bad Request" %}
Workflow ID is missing from the request path

```json
{
    "message": "incorrect workflow id"
}
```

{% endtab %}
{% endtabs %}

## resumeWorkflow

<mark style="color:green;">`POST`</mark> `https://api.looply.io/v2/workflows/resumeWorkflow/{process_id}`

Manually resume workflows depending on the process\_id supplied. Resuming a workflow can contain any JSON stringified body payload, `source` is the only protected attribute.&#x20;

#### Path Parameters

| Name                                          | Type   | Description                                                                                            |
| --------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------ |
| process\_id<mark style="color:red;">\*</mark> | string | `process_id` supplied to the `triggerworkflow` or the `looply_execution_id` if you did not supply one. |

#### Headers

| Name                                        | Type   | Description    |
| ------------------------------------------- | ------ | -------------- |
| x-api-key<mark style="color:red;">\*</mark> | string | Looply API Key |

#### Request Body

| Name                                             | Type   | Description                                         |
| ------------------------------------------------ | ------ | --------------------------------------------------- |
| payload<mark style="color:red;">\*</mark>        | object | Payload for workflow                                |
| payload.source<mark style="color:red;">\*</mark> | string | Source of request - must be either `SAP` or `TEAMS` |

#### Example Body

```json
{
    "payload": {
        "source":"SAP", // or "TEAMS" -  Source is a protected attribute
        // Any other relevant data for your workflow here...
    }
}
```

{% tabs %}
{% tab title="200 OK" %}

```json
{
  "message": "success", 
  "code": "WORKFLOW_RESTARTED",
  "data": {
    "workflow_identifier": {
      "organization_id": "org-123",
      "workflow_id": "workflow-123",
      "workflow_execution_id": "execution-123"
    }
  }
}
```

{% endtab %}

{% tab title="404 Not Found " %}
A workflow execution could not be found with the specified process ID for your organization.&#x20;

**EXECUTION\_NOT\_FOUND**

```json
{
  "code": "EXECUTION_NOT_FOUND",
  "data": {
    "organization_id": "org-123"
  }
}
```

{% endtab %}

{% tab title="403 Forbidden" %}
The API key provided is missing or invalid and access has been denied

```json
{
    "message": "Forbidden"
}
```

{% endtab %}

{% tab title="409 Conflict" %}
Workflow execution could not be resumed due to a conflict.&#x20;

**EXECUTION\_IN\_PROGRESS**

An execution with this process ID is in progress and cannot be resumed right now - this may be that your workflow execution has not yet reached it's wait state or moved past it.&#x20;

```json
{
  "code": "EXECUTION_IN_PROGRESS",
  "data": {
    "execution_id": "execution-123",
    "workflow_id": "workflow-123",
    "workflow_status": "IN_PROGRESS",
    "workflow_current_state": "some-state",
    "workflow_process_id": "process-123",
    "execution_description": "description",
    "timestamp": "2023-06-15T12:34:56.789Z"
  }
}
```

**NO\_RESUMABLE\_EXECUTION**

All workflow executions with this process ID have completed and cannot be resumed.

```json
{
  "code": "NO_RESUMABLE_EXECUTION",
  "data": {
    "execution_key": "execution-123"
  }
}
```

**ALREADY\_RESTARTED**

This workflow execution has just recently restarted with the provided resume token.&#x20;

```json
{
  "code": "ALREADY_RESTARTED",
  "data": {
    "execution_key": "execution-123"
  }
}
```

{% endtab %}

{% tab title="500 Internal Server Error" %}
A server error has occurred preventing this workflow from being resumed. Contact Looply Support.&#x20;

**WORKFLOW\_RESTART\_FAILED**

We were unable to restart your workflow due to a fatal server error.&#x20;

```json
{
  "code": "WORKFLOW_RESTART_FAILED",
  "data": {
    "execution_key": "execution-123"
  }
}
```

**MISSING\_RESUME\_TOKEN**

We were unable to restart your workflow due to an error with your execution resume token.&#x20;

```json
{
  "code": "MISSING_RESUME_TOKEN",
  "data": {
    "execution_key": "execution-123"
  }
}
```

{% endtab %}
{% endtabs %}

## terminateWorkflow

<mark style="color:green;">`POST`</mark> `https://api.looply.io/v2/workflows/terminateWorkflow/{process_id}`

Terminate an ongoing execution of your workflow - supports providing a termination reason.&#x20;

**Path Parameters**

| Name                                          | Type   | Description                                                                                           |
| --------------------------------------------- | ------ | ----------------------------------------------------------------------------------------------------- |
| process\_id<mark style="color:red;">\*</mark> | string | `process_id` supplied to the `triggerworkflow`or the `looply_execution_id` if you did not supply one. |

**Headers**

| Name                                        | Type   | Description    |
| ------------------------------------------- | ------ | -------------- |
| x-api-key<mark style="color:red;">\*</mark> | string | Looply API Key |

#### Request Body

<table><thead><tr><th width="264">Name</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>payload<mark style="color:red;">*</mark></td><td>object</td><td>Payload for workflow</td></tr><tr><td>payload.termination_reason</td><td>string</td><td>Optional reason for termination - defaults to <code>"Workflow execution terminated by user."</code> </td></tr></tbody></table>

**Example Body**

```json
{
    "payload": {
        "termination_reason": "Process has now expired."
    }
}
```

{% tabs %}
{% tab title="200" %}

```json
{
    "message": "terminated"
}
```

{% endtab %}

{% tab title="404" %}

```json
{
    // Execution or process ID supplied in path is incorrect
    // or exection is not actively running
    "message": "execution not found"
}
```

{% endtab %}
{% endtabs %}

## toggleScheduledWorkflow

<mark style="color:green;">`POST`</mark> `https://api.looply.io/v2/workflows/toggleScheduledWorkflow`

Pause and resume scheduled workflows.

#### Headers

| Name      | Type   | Description    |
| --------- | ------ | -------------- |
| x-api-key | string | Looply API Key |

#### Request Body

| Name              | Type   | Description             |
| ----------------- | ------ | ----------------------- |
| workflow\_id      | String | id of the workflow      |
| workflow\_version | Number | version of the workflow |
| action            | String | `PAUSE` or `PLAY`       |

{% tabs %}
{% tab title="200: OK Success" %}
Example Payload:

```json
{
	"message": "success",
	"workflow_id": "be9439c2-16b9-4de2-bb1a-9154b85af065",
	"workflow_version": 1,
	"status": "ACTIVE" // or "PAUSED"
}
```

{% endtab %}

{% tab title="400: Bad Request Incorrect input body provided" %}

{% endtab %}

{% tab title="409: Conflict Wrong Action Provided." %}

{% endtab %}

{% tab title="500: Internal Server Error Server side error." %}

{% endtab %}
{% endtabs %}

## getWorkflowExecutionById

<mark style="color:blue;">`GET`</mark> `https://api.looply.io/v2/workflows/getWorkflowExecutionById`

Returns the current state and logs for the workflow execution.

#### Query Parameters

| Name                                                      | Type   | Description                   |
| --------------------------------------------------------- | ------ | ----------------------------- |
| workflow\_id<mark style="color:red;">\*</mark>            | String | Id of the workflow            |
| workflow\_execution\_id<mark style="color:red;">\*</mark> | String | Id for the workflow execution |

#### Headers

| Name                                        | Type   | Description    |
| ------------------------------------------- | ------ | -------------- |
| x-api-key<mark style="color:red;">\*</mark> | String | Looply API Key |

{% tabs %}
{% tab title="200: OK Success" %}
Example Payload:

```json
{
	"message": "success",
	"item": {
		"workflow_current_state": "State Name",
		"organization_id": "****",
		"workflow_execution_id": "****",
		"modified_by": null,
		"workflow_version": 1,
		"workflow_event_log": {
			// trimmed down logs
		},
		"workflow_id": "****",
		"workflow_input_payload": {
			// combined input payload
		},
		"workflow_output_payload": {
			// combined output payload
		},
		"workflow_trigger_type": "REQUEST",
		"workspace_id": "****",
		"created_on": 1697641835712,
		"condensed_logs": {
            		// input/output for each step. Condensed
        	},
		"workflow_details": { 
			// structure of the workflow - connections and steps
		}
    }
```

{% endtab %}

{% tab title="400: Bad Request Incorrect or missing Query Parameters" %}

{% endtab %}

{% tab title="404: Not Found Execution does not exist" %}

{% endtab %}

{% tab title="500: Internal Server Error Server side Error" %}

{% endtab %}

{% tab title="401: Unauthorized You don't have access to this execution." %}

{% endtab %}

{% tab title="403: Forbidden Incorrect or No API Key Supplied" %}

{% endtab %}
{% endtabs %}

## getWorkflowExecutionHistory

<mark style="color:blue;">`GET`</mark> `https://api.looply.io/v2/workflows/getWorkflowExecutionHistory`

Returns a list of workflow executions. Will return a `lastKey` attribute. Use this to get the next page of data.&#x20;

Limit can be added to the request, 30 is the max.

#### Query Parameters

| Name                                           | Type   | Description                                          |
| ---------------------------------------------- | ------ | ---------------------------------------------------- |
| workflow\_id<mark style="color:red;">\*</mark> | string | id of the workflow                                   |
| limit                                          | string | Number of items to return per page. Defaults to `20` |
| workflow\_execution\_id                        | string | Pagination Key from the `lastKey` attribute.         |

#### Headers

| Name                                        | Type   | Description    |
| ------------------------------------------- | ------ | -------------- |
| x-api-key<mark style="color:red;">\*</mark> | string | Looply API Key |

{% tabs %}
{% tab title="200: OK Success" %}
Example Payload:

```json
{
	"message": "success",
	"count": 20,
	"items": [
		{
			"workflow_id": "****",
			"workflow_current_state": "wait_for_response_state",
			"modified_on": 1697454831391,
			"organization_id": "****",
			"workflow_execution_id": "****",
			"modified_by": null,
			"workflow_version": 1,
			"workflow_event_log": {
				// trimmed down logs
			},
			"workflow_trigger_type": "REQUEST",
			"workspace_id": "****",
			"created_on": 1697454828765,
			"workflow_details": {
				"workflow_name": "Workflow name"
			}
		}
		
	],
	"lastKey": {
		"workflow_id": "****",
		"workflow_execution_id": "****"
	}
}
```

{% endtab %}

{% tab title="400: Bad Request Incorrect or missing Query Parameters" %}

{% endtab %}

{% tab title="401: Unauthorized You don't have access to this workflow's executions." %}

{% endtab %}

{% tab title="403: Forbidden Incorrect or No API Key Supplied" %}

{% endtab %}

{% tab title="500: Internal Server Error Server side error." %}

{% endtab %}
{% endtabs %}

## getOrganizationExecutions

<mark style="color:blue;">`GET`</mark> `https://api.looply.io/v2/workflows/getOrganizationExecutions`

Returns a list of all the workflow executions for an organization. Will return a `lastKey` attribute. Use this to get the next page of data.&#x20;

Limit can be added to the request, 30 is the max.

#### Query Parameters

| Name                    | Type   | Description                                          |
| ----------------------- | ------ | ---------------------------------------------------- |
| limit                   | string | Number of items to return per page. Defaults to `20` |
| workflow\_id            | string | Pagination Key from the `lastKey` attribute          |
| workflow\_execution\_id | string | Pagination Key from the `lastKey` attribute          |

{% tabs %}
{% tab title="200: OK Success" %}
Example Payload:

```json
{
	"message": "success",
	"count": 20,
	"items": [
		{
			"workflow_id": "****",
			"workflow_current_state": "wait_for_response_state",
			"modified_on": 1697454831391,
			"organization_id": "****",
			"workflow_execution_id": "****",
			"modified_by": null,
			"workflow_version": 1,
			"workflow_event_log": {
				// event log
			},
			"workflow_trigger_type": "REQUEST",
			"workspace_id": "****",
			"created_on": 1697454828765,
			"workflow_details": {
				"workflow_name": "Workflow name"
			}
		},
		
	],
	"lastKey": {
		"workflow_id": "****",
		"workflow_execution_id": "****"
	}
}
```

{% endtab %}

{% tab title="403: Forbidden Incorrect or No API Key Supplied" %}

{% endtab %}

{% tab title="500: Internal Server Error Server side error." %}

{% endtab %}
{% endtabs %}

## getWorkflowById

<mark style="color:blue;">`GET`</mark> `https://api.looply.io/v2/workflows/getWorkflowById`

Returns all data for a Looply Workflow

#### Query Parameters

| Name                                                | Type   | Description             |
| --------------------------------------------------- | ------ | ----------------------- |
| workflow\_id<mark style="color:red;">\*</mark>      | string | Id for the workflow     |
| workflow\_version<mark style="color:red;">\*</mark> | string | Version of the workflow |

#### Headers

| Name                                        | Type   | Description    |
| ------------------------------------------- | ------ | -------------- |
| x-api-key<mark style="color:red;">\*</mark> | string | Looply API Key |

{% tabs %}
{% tab title="200: OK Success" %}
Example Payload:

```json
{
  "message": "success",
  "item": {
    "organization_id": "****",
    "workflow_integrations": {
      // integrations configured for this workflow - e.g. SAP profile
    },
    "workflow_name": "****",
    "created_by": "test-user",
    "workflow_status_version": "DRAFT_1",
    "modified_by": null,
    "workflow_version": 1,
    "workflow_steps": [
      // states within the workflow
    ],
    "workflow_id": "****",
    "modified_on": 1697642922168,
    "workflow_connections": [
      // connections between all the workflow states
    ],
    "workflow_status": "DRAFT",
    "latest": "true",
    "workspace_id": "****",
    "created_on": 1695136500202,
    "workflow_schema": {
      // workflow schema
    }
  }
}

```

{% endtab %}

{% tab title="400: Bad Request Incorrect or missing Query Parameters" %}

{% endtab %}

{% tab title="401: Unauthorized You don't have access to this workflow." %}

{% endtab %}

{% tab title="403: Forbidden Incorrect or No API Key Supplied" %}

{% endtab %}

{% tab title="404: Not Found Execution does not exist" %}

{% endtab %}

{% tab title="500: Internal Server Error Server side Error" %}

{% endtab %}
{% endtabs %}

## getOrganizationWorkflows

<mark style="color:blue;">`GET`</mark> `https://api.looply.io/v2/workflows/getOrganizationWorkflows`

Returns a list organization workflows. Will return a `lastKey` attribute. Use this to get the next page of data.&#x20;

Limit can be added to the request, 30 is the max.

#### Query Parameters

| Name              | Type   | Description                                    |
| ----------------- | ------ | ---------------------------------------------- |
| limit             | string | Number of items to return per page. Default 20 |
| workflow\_id      | string | Pagination Key from the `lastKey` attribute.   |
| workflow\_version | string | Pagination Key from the `lastKey` attribute.   |

#### Headers

| Name                                        | Type   | Description    |
| ------------------------------------------- | ------ | -------------- |
| x-api-key<mark style="color:red;">\*</mark> | string | Looply API Key |

{% tabs %}
{% tab title="200: OK Success" %}
Example Payload:

```json
{
  "message": "success",
  "count": 20,
  "items": [
    {
      "workflow_id": "****",
      "modified_on": 1693488718083,
      "organization_id": "****",
      "workflow_status": "ACTIVE",
      "latest": "false",
      "workflow_name": "Adaptive Card Workflow",
      "created_by": "****",
      "workflow_status_version": "ACTIVE_3",
      "modified_by": "****",
      "workspace_id": "****",
      "created_on": 1692614072550
    }
  ],
  "lastKey": {
    "organization_id": "****",
    "workflow_id": "****",
    "workflow_version": 1
  }
}
```

{% endtab %}

{% tab title="500: Internal Server Error Server side Error" %}

{% endtab %}
{% endtabs %}

## getWorkflowSchemaById

<mark style="color:blue;">`GET`</mark> `https://api.looply.io/v2/workflows/getWorkflowSchemaById`

Returns the data schema for a workflow

#### Query Parameters

| Name                                                | Type   | Description             |
| --------------------------------------------------- | ------ | ----------------------- |
| workflow\_id<mark style="color:red;">\*</mark>      | string | Id of the workflow      |
| workflow\_version<mark style="color:red;">\*</mark> | string | Version of the workflow |

#### Headers

| Name                                        | Type   | Description    |
| ------------------------------------------- | ------ | -------------- |
| x-api-key<mark style="color:red;">\*</mark> | string | Looply API Key |

{% tabs %}
{% tab title="200: OK Success" %}
Example Payload:

```json
{
  "message": "success",
  "item": {
    "workflow_id": "****",
    "modified_on": 1697642922168,
    "created_by": "test-user",
    "workflow_mock_payload": {
      // mock payload from a previous execution
    },
    "workflow_schema": {
      // schema for the mock payload
    },
    "modified_by": null,
    "workflow_version": 1,
    "created_on": 1695136500202
  }
}

```

{% endtab %}

{% tab title="400: Bad Request Incorrect or missing Query Parameters" %}

{% endtab %}

{% tab title="401: Unauthorized You don't have access to this execution." %}

{% endtab %}

{% tab title="403: Forbidden Incorrect or No API Key Supplied" %}

{% endtab %}

{% tab title="404: Not Found Execution does not exist" %}

{% endtab %}

{% tab title="500: Internal Server Error Server side Error" %}

{% endtab %}
{% endtabs %}

## getAppUserInstallationStatus

<mark style="color:blue;">`GET`</mark> `https://api.looply.io/v2/workflows/getAppUserInstallationStatus`

Returns information for if the specified user has an app installed within your organisation

#### Query Parameters

| Name                                      | Type   | Description           |
| ----------------------------------------- | ------ | --------------------- |
| email<mark style="color:red;">\*</mark>   | string | Email address of user |
| app\_id<mark style="color:red;">\*</mark> | string | ID of the app         |

#### Headers

| Name                                        | Type   | Description    |
| ------------------------------------------- | ------ | -------------- |
| x-api-key<mark style="color:red;">\*</mark> | string | Looply API Key |

{% tabs %}
{% tab title="200: OK Success" %}
Example Payload:

```json
{
  "message": "success",
  "email": "user@organization.com", 
  "app_id": "1234", 
  "installed": "true"
}
```

{% endtab %}

{% tab title="400: Bad Request Incorrect or missing Query Parameters" %}
Example Response

```json
{
  "message": "Missing required parameters for this request."
}
```

{% endtab %}

{% tab title="403: Forbidden Incorrect or No API Key Supplied" %}
Example Response

```json
{
  "message": "Forbidden"
}
```

{% endtab %}

{% tab title="500: Internal Server Error Server side Error" %}
Example Response

```json
{
  "message": "something went wrong"
}
```

{% endtab %}
{% endtabs %}


# Adaptive Card API

Adaptive Card related endpoints

## getCardById

<mark style="color:blue;">`GET`</mark> `https://api.looply.io/v1/adaptive-cards/getCardById`

Returns the Adaptive Card JSON Payload and related data.

#### Query Parameters

| Name                                            | Type   | Description         |
| ----------------------------------------------- | ------ | ------------------- |
| card\_id<mark style="color:red;">\*</mark>      | String | id for the card     |
| card\_version<mark style="color:red;">\*</mark> | String | version of the card |

#### Headers

| Name                                        | Type   | Description  |
| ------------------------------------------- | ------ | ------------ |
| x-api-key<mark style="color:red;">\*</mark> | String | Your API Key |

{% tabs %}
{% tab title="200: OK Success" %}
Example Payload

```json
{
  "message": "success",
  "item": {
    "organization_id": "****",
    "created_by": "****",
    "status": "DRAFT",
    "modified_by": "****",
    "workflow_version": 1,
    "card_id": "****",
    "workflow_id": "****",
    "card_name": "Card Name",
    "modified_on": 1697454380810,
    "status_version": "DRAFT-1",
    "latest": "true",
    "card_payload": {
      // Adaptive Card JSON Payload
    },
    "card_version": 1,
    "workspace_id": "****",
    "created_on": 1695034193817
  }
}

```

{% endtab %}

{% tab title="401: Unauthorized You don't have access to this adaptive card." %}

{% endtab %}

{% tab title="403: Forbidden Incorrect or No API Key Supplied" %}

{% endtab %}

{% tab title="404: Not Found Card does not exist." %}

{% endtab %}

{% tab title="500: Internal Server Error Server side error." %}

{% endtab %}

{% tab title="400: Bad Request Incorrect or missing Query Parameters" %}

{% endtab %}
{% endtabs %}

## getLatestActiveCard

<mark style="color:blue;">`GET`</mark> `https://api.looply.io/v1/adaptive-cards/getLatestActiveCard`

Returns the latest active card.

#### Query Parameters

| Name                                       | Type   | Description     |
| ------------------------------------------ | ------ | --------------- |
| card\_id<mark style="color:red;">\*</mark> | String | id for the card |

{% tabs %}
{% tab title="400: Bad Request Incorrect or missing Query Parameters" %}

{% endtab %}

{% tab title="200: OK Success" %}
Example Payload:

```json
{
  "message": "success",
  "item": {
    "organization_id": "****",
    "created_by": "****",
    "status": "DRAFT",
    "modified_by": "****",
    "workflow_version": 1,
    "card_id": "****",
    "workflow_id": "****",
    "card_name": "Card Name",
    "modified_on": 1697454380810,
    "status_version": "DRAFT-1",
    "latest": "true",
    "card_payload": {
      // Adaptive Card JSON Payload
    },
    "card_version": 1,
    "workspace_id": "****",
    "created_on": 1695034193817
  }
}
```

{% endtab %}

{% tab title="401: Unauthorized You don't have access to this adaptive card." %}

{% endtab %}

{% tab title="403: Forbidden Incorrect or No API Key Supplied" %}

{% endtab %}

{% tab title="404: Not Found Card does not exist." %}

{% endtab %}

{% tab title="500: Internal Server Error Server side error." %}

{% endtab %}
{% endtabs %}

## getOrganizationCards

<mark style="color:blue;">`GET`</mark> `https://api.looply.io/v1/adaptive-cards/getOrganizationCards`

Returns a list of all the cards created in the organization. Will return a `lastKey` attribute. Use this to get the next page of data.&#x20;

Limit can be added to the request, 30 is the max.

#### Query Parameters

| Name          | Type   | Description                                    |
| ------------- | ------ | ---------------------------------------------- |
| limit         | String | Number of items to return per page. Default 20 |
| card\_id      | String | Pagination Key from the `lastKey` attribute    |
| card\_version | String | Pagination Key from the `lastKey` attribute    |

#### Headers

| Name                                        | Type   | Description  |
| ------------------------------------------- | ------ | ------------ |
| x-api-key<mark style="color:red;">\*</mark> | String | Your API Key |

{% tabs %}
{% tab title="200: OK Success" %}
Example Payload:

```json
{
  "message": "success",
  "count": 20,
  "items": [
    {
      "card_name": "Card name",
      "status_version": "DRAFT-1",
      "organization_id": "****",
      "latest": "true",
      "created_by": "****",
      "status": "DRAFT",
      "card_version": 1,
      "card_id": "****",
      "workspace_id": "****",
      "created_on": 1693234726646
    }
  ],
  "lastKey": {
    "organization_id": "****",
    "card_id": "****",
    "card_version": 1
  }
}

```

{% endtab %}

{% tab title="500: Internal Server Error Server side Error" %}

{% endtab %}
{% endtabs %}


# OAuth Authentication


# Managing Organisations

Manage and invite users to your Looply organisation

Explore the power of collaborative efficiency within Looply's **Team Manager**, designed for organisational leaders.&#x20;

Admin users can seamlessly oversee and manage their organisation's team members, shaping collaboration dynamics. From extending invitations to controlling access levels through user roles, the Team Manager is your hub for organisational synergy. Dive into a workspace where integrations, workflows, apps, and adaptive card templates are shared seamlessly, fostering a united and productive team.

{% hint style="info" %}
**Note:** The Looply Team Manager is only available to organisation admins.&#x20;
{% endhint %}

## Inviting New Team Members

Looply's Team Manager allows admin users to invite new team members to join their organisation and facilitate collaboration.&#x20;

To invite a new team member, click the **New Team Member** button located on the Team Manager page.&#x20;

<figure><img src="/files/bJt5eLSukDLgSsIIeYQb" alt=""><figcaption></figcaption></figure>

### Create Invite

To invite users to join your Looply organisation, you simply need to provide their **email address** and specify a **role** for that user.&#x20;

<figure><img src="/files/blNASe16w9Ml0dM8iWB1" alt=""><figcaption></figcaption></figure>

See [Team Roles and Permissions](/team-management/team-roles-and-permissions) for more information about roles within Looply.&#x20;

Once you have entered at least one email address and role, you can send your invites by clicking the **Send invites** button.&#x20;

### Revoke Invite

You can revoke the invitation of a user at any time before it has been accepted by clicking on the overflow menu next to the invited user and selecting the **Revoke Invite** option.&#x20;

This user's invitation will be immediately revoked and they will not be able to use it to join your organisation.&#x20;

### Resend Invite

Need to issue a new invitation link to your team member?&#x20;

Simply click the overflow menu next to the user within the Team Manager and select the **Resend Invite** option.&#x20;

## Managing Team Members

In our Looply Team Manager, organisation admins gain a comprehensive overview of their team's dynamics.&#x20;

This feature-rich space enables admins to track invitation statuses, view assigned roles, monitor authentication levels, and access last login dates for each team member.&#x20;

Empowered with these insights, admins can seamlessly adjust roles, ensuring optimal collaboration. Furthermore, the ability to disable user accounts adds an extra layer of control, offering a streamlined approach to organisational governance.

### Update Roles

Effortlessly tailor your team's dynamics with Looply's role adjustment feature.&#x20;

View and amend roles for any non-admin team members by accessing the **Edit Roles** option within the user overflow menu.&#x20;

The intuitive **Edit Team Member** dialog presents a straightforward interface for role adjustments. Admins can seamlessly save their changes with a click of the **Confirm** button, ensuring the team's structure aligns precisely with organisational needs.

### Access Control

Admins can seamlessly disable access for any non-admin team member, immediately restricting login and application usage.&#x20;

A team member's account can be disabled by accessing the overflow menu and clicking the **Disable** option.&#x20;

To reinstate access, a simple selection of the **Restore** option re-enables team member accounts, offering a flexible approach to team management.


# Team Roles and Permissions

Explore team roles in Looply: Define access, empower collaboration.

Dive into Looply's Team Roles and Permissions to understand the distinct roles shaping your organisation's collaborative landscape, from Admins with full control to specialised user access roles.

See [Managing Organisations](/team-management/managing-organisations) for more information on assigning roles to your users.&#x20;

## Roles

### Admin

Admins have **full read/write access** within your organisation's Looply workspaces.&#x20;

Admins can manage your organisation and invite users to join your team.&#x20;

### Developer

Developers have **limited read/write access** within your organisation's Looply workspaces.&#x20;

Developers are unable to access the Team Manager or invite users to join their team.&#x20;

## Permissions

|                   | Admin                | Developer                                                                                                                                                                                 |
| ----------------- | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| App Manager       | :white\_check\_mark: | <p><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span> User Manager<br><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span> Installation History</p> |
| Template Designer | :white\_check\_mark: | :white\_check\_mark:                                                                                                                                                                      |
| Template Vault    | :white\_check\_mark: | :white\_check\_mark:                                                                                                                                                                      |
| Workflow Studio   | :white\_check\_mark: | :white\_check\_mark:                                                                                                                                                                      |
| Workflow Builder  | :white\_check\_mark: | :white\_check\_mark:                                                                                                                                                                      |
| Workflow History  | :white\_check\_mark: | :white\_check\_mark:                                                                                                                                                                      |
| Dashboard         | :white\_check\_mark: | :white\_check\_mark:                                                                                                                                                                      |
| Billing           | :white\_check\_mark: | :x:                                                                                                                                                                                       |
| Integrations      | :white\_check\_mark: | Read-only                                                                                                                                                                                 |
| Team Manager      | :white\_check\_mark: | :x:                                                                                                                                                                                       |


# JavaScript Libraries

Looply provides support for a range of pre-defined JavaScript libraries that can be accessed by developers in custom functions during workflow design.&#x20;

See [Using Functions](/workflows/using-functions) for more information on function steps in Looply workflows.&#x20;

You can find the full list of supported libraries below. If you require a specific library not included in this list, get in touch with us for support.&#x20;

* [uuid](https://www.npmjs.com/package/uuid)


# Creating MS Teams Apps

Learn how to create, manage, deploy and delete a Teams app with Looply

{% @arcade/embed flowId="TrIkjVRLTfmhTNVP3YOK" url="<https://app.arcade.software/share/TrIkjVRLTfmhTNVP3YOK>" fullWidth="true" %}


# Designing Workflows

Learn how to create and design a simple workflow in Looply

In this tutorial, we will walkthrough creating a simple workflow that will:&#x20;

* Receive a PDF document as a Base64 encoded string
* Call an external API to parse the document
* Write and run custom function code
* Use an if condition to check the document type
* Dispatch adaptive cards for each outcome of the condition

{% @arcade/embed flowId="PnF6yMPyDK9JI8qq6svm" url="<https://app.arcade.software/share/PnF6yMPyDK9JI8qq6svm>" fullWidth="true" %}


# Building Adaptive Cards

Create beautiful data driven Adaptive Cards with Looply

{% @arcade/embed flowId="GLMcVNsgm8hiDoylcy7r" url="<https://app.arcade.software/share/GLMcVNsgm8hiDoylcy7r>" fullWidth="true" %}


# Adaptive Cards with AI

Step-by-step guide to creating and refining AI-generated Adaptive Cards.

{% @arcade/embed flowId="9UfUSA3M6Hya7vbn1p5V" url="<https://app.arcade.software/share/9UfUSA3M6Hya7vbn1p5V>" fullWidth="true" %}


# Examining Workflow Executions

Get an in-depth view into how well a Workflow has executed.

{% @arcade/embed flowId="QOUY9TIHh2ddOdIKMstC" url="<https://app.arcade.software/share/QOUY9TIHh2ddOdIKMstC>" fullWidth="true" %}


# Governance


# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

#### **2.8.1 (01-04-2026)**

#### **🚀 New Features**

**App Manager**

* **Installation Health Check (Beta):** New diagnostic tab that performs a multi-step health check against Microsoft Graph and Bot Framework — verifying app installation, conversation context, connection liveness, and bot accessibility for each user.
* **Blocked Bot Detection:** Health checks detect if a user has blocked the bot in Teams — surfaced as a clear "Blocked" status in the health table.
* **Self-Healing Conversations:** When a health check detects a stale conversation, Looply automatically re-registers it via the Bot Framework REST API — recovering the connection without manual reinstallation.
* **Test Message Delivery:** Admins can send a test message to individual users directly from the health table to verify end-to-end message delivery, with the option to delete the message afterwards.
* **Profile Sync:** New sync button in the Installation Health tab fetches the latest user profile (name, email, job title) from Microsoft Graph and updates the local record.
* **Activity Tracking:** Each admin action (health check, test message, profile sync, message deletion) is recorded with a timestamp and displayed in the health table for audit visibility.

**Adaptive Card Step**

* **Self-Healing Conversation Recovery:** If Microsoft Teams loses track of a conversation, the bot now automatically recovers and retries the operation. Previously, this resulted in a permanent "Conversation not found" failure requiring the end user to manually re-install the app. Applies to SEND, UPDATE, and DELETE actions.

#### **🛠️ Improvements**

**Adaptive Card Step**

* **Partial Delivery Tracking:** Workflow history now records the full picture when a step partially succeeds — including successful deliveries, the failure, and any skipped recipients.
* **Recipient Email in Error Messages:** Error messages now include the recipient email address for easier troubleshooting.
* **Accurate Step Error Messages:** Step errors now show the actual failure reason per recipient instead of a generic "Configuration error".
* **Successful DELETE Tracking:** Successful deletions are now recorded in workflow history, matching existing SEND and UPDATE behaviour.
* **Graceful Handling of Missing Cards:** UPDATE and DELETE steps now skip recipients whose original SEND failed, with a clear message instead of a cryptic error.
* **Missing Recipient Tracking:** UPDATE and DELETE steps now record recipients not found in the original SEND instead of silently dropping them.
* **Zero-Success Guard:** UPDATE and DELETE steps now fail when all recipients fail or are missing — matching existing SEND behaviour.

**Workflow History**

* **Workflow History Loading:** Extended retry strategy for Aurora cold starts with more attempts and longer delays, keeping the loading spinner active instead of showing an error.

#### **🪲 Bug Fixes**

**Workflow History**

* **Workflow Step Status:** Fixed a bug where failed workflow steps with warning logs were incorrectly displayed in amber instead of red on the history canvas.

**Adaptive Card Step**

* **"Continue if action fails" Toggle:** Fixed the toggle being bypassed for certain error types.
* **Delivery Record Preservation:** Fixed workflow history losing delivery records when a step fails — successful deliveries before the failure are now preserved.
* **DELETE Error Messages:** Fixed DELETE error messages showing a generic "Configuration error" instead of the actual error reason.

#### 2.8.0 (13-03-2026)

#### **🚀 New Features**

* **Dynamic Field Encryption:** Integration profiles now support marking custom connection parameter fields as passwords — these are automatically secured on create/update and masked in API responses
* **Billing Plan Filtering:** Connected organization integrations are now filtered by the organization's billing plan, only showing integrations included in the plan
* **Workflow Log Retention:** Configure automatic log retention policies per workflow with status-specific rules — keep successful runs for 30 days, keep failed runs for 90 days, or any combination you need
* **Organisation Default Retention:** Set a default log retention policy at the organisation level that applies to all workflows unless overridden
* **Execution Log Archiving:** Expired execution logs are automatically archived before purging, so you can still access historical data when needed
* **Workflow Soft-Delete:** Deleting a workflow now safely removes all associated executions and logs in the background, preventing orphaned data
* **Dashboard Time Period Filtering:** Filter dashboard stats by week, month, quarter, year, or all time with calendar-based boundaries (week starts Monday)
* **Dashboard User Preferences:** Save and persist per-user dashboard preferences for time period and archived execution visibility
* **Archived Executions in Dashboard:** Optionally include archived workflow executions in dashboard statistics and counts
* **TERMINATED Execution Status:** Track and display TERMINATED as a distinct execution status in dashboard stats
* **Bulk App Installation:** Install Teams apps across multiple users in bulk with background job processing
* **Bulk Install Job Management:** Query bulk install jobs by app with pagination, track per-user installed/requested status, and monitor job progress

#### **🛠️ Improvements**

**Workflow Designer**

* **Adaptive card step:** Custom integrations are now supported when sending and updating adaptive cards
* **Choice state:** Improved variable binding path resolution for more reliable conditional branching
* **HTTP Request step:** Added CSRF token support for SAP HTTP requests with secure token handling
* **Token exchange:** Support for chained token exchanges when integrating with services that require multi-step authentication, with cascade refresh strategy and individual hop expiry tracking
* **Single-hop token refresh:** Added Basic Auth support for single-hop OAuth token refresh flows
* **Custom integrations:** Added option to skip token refresh for integrations that don't support it

**Auth Service**

* **Teams SDK upgrade:** Migrated all auth pages to the latest Teams SDK
* **Auth UI:** Branded loading spinner and success checkmark on login completion instead of blank page flash
* **Token storage:** Simplified token storage for improved reliability and reduced payload size
* **Token expiry:** Added access token expiry tracking to the auth response

**Workflow History**

* **Pagination:** Archived execution counts are now included in pagination for a complete view of workflow history
* **Redriven executions:** Dashboard no longer overwrites execution status after a workflow is redriven, preserving the correct state

**Dashboard**

* **App Installation Counts:** Added pagination when querying app installations to ensure accurate counts for large datasets
* **Archived Workflow Executions:** Query support for retrieving archived execution data by organisation

#### **🪲 Bug Fixes**

* **Organization isolation:** Improved organization scoping on custom integration queries
* **Image URL field:** Fixed integration image handling when creating new versions
* **Integration profiles:** Authorization error messages now include context about which integration profile failed
* **Log retention:** Fixed format handling issues that could prevent the purge system from processing retention rules correctly
* **Redriven executions:** Stale cached archive data is now bypassed for redriven executions that are still in flight
* **Adaptive card removal:** Fixed card removal to prevent orphaned state on partial failures
* **Card update recipients:** Filter out recipients without a message ID to prevent card update failures
* **User mail normalization:** Improved email address normalization during bot member events
* **Member added handler:** Fixed user data handling when new members are added

### 2.7.0 (19-12-2025)

#### 🚀 New Features

* **Custom Integration Profiles:** Organizations can now create profiles for published custom integrations to connect with external systems.&#x20;
* Billing plan-based integration filtering to control access based on subscription tier

#### 🛠️ Improvements

* **Workflow Designer**
  * **Adaptive card step:** Now supports dynamic authentication to external third party system. A variety of authentication scenarios are covered.&#x20;
  * **HTTP Step:**&#x20;
    * Now supports custom integration binding for custom integrations
    * Basic authentication headers can be dynamically bound to environment, global, payload variables
* **Error Handling**
  * Improved error handling for failures
  * Improved error handling for bot framework
  * Better execution handling for long running processes&#x20;
* **Security**
  * Enhanced encryption for integration profiles
  * Improved monitoring infrastructure to track long running processes
  * Enhanced workspace authorisation checks

#### 🪲 Bug Fixes

* Fixed Teams message update timeout due to bot framework latency
* Fixed TTL calculation for debounce checks
* Fixed environment variable resolution for advanced conditional steps
* Fixed logic operator grouping for advanced conditionals
* Fixed rule group separation&#x20;
* Fixed type conversion for numeric values

### 2.6.0 (18-07-2025)

#### 🚀 New Features

* **Import/Export Workflows:** Easily transfer and manage your Looply workflows across different environments with our new import/export functionality
* **Mock Workflow Payloads:** Create mock payload data and use it in your Looply workflow executions to test/validate your workflows without live data
* **Mock Step Response:** Simulate outputs from  workflow steps for comprehensive testing/building without external dependencies or systems
* **Workflow Definition/Schema Editing:** Access your workflow definition/schema JSON code for advanced customisation/debugging&#x20;

### 2.5.0 (20-06-2025)

#### 🚀 New Features

**Notification Lifecycle Management**

* Organization admins can search & filter Looply notifications to manage their lifecycle with our manual clean-up process for selected or bulk deletions
* Simulate system notification deletions with dry run operations
* Monitor the status and progress of deletion requests in real-time with a comprehensive breakdown of each process and detailed statistics

#### 🛠️ Improvements

* Added support for conditionally sending workflow error notifications via payload, datastore or environment variable values
* Improved the resumeWorkflow developer API response with internal Looply codes
* Additional data added to Looply workflow error notification emails & webhook responses

### 2.4.0 (25-04-2025)

#### 🚀 New Features

**Workflow History Redesign**

A completely rebuilt Workflow History page with:

* Advanced filters by status and date range
* Improved pagination controls
* Enhanced search by process ID, workflow ID, and name
* Support for URL parameters to restore previous searches
* Easier navigation between newest and oldest records<br>

**Search Behaviour Update**

* Partial match search now requires an asterisk (\*) before or after the term. This replaces the previous automatic wildcard behaviour for better performance and control.

#### 🛠️ Improvements

* **Developer API:** Optimized performance & enhanced security measures when triggering, resuming & terminating workflows

#### 🪲 Bug Fixes

* Fixed an issue where attempting to send invalid adaptive card templates resulted in an incorrect 'App Not Available' error
* Fixed an issue with renaming datastores
* Fixed an issue where undefined/null values would cause an error when being evaluated in Advanced Conditional steps instead of acting as false values
* Fixed an issue where workflows would incorrectly detect an SAP integration as active even after all SAP integration steps were deleted, causing execution to be blocked if no SAP integration profile had been configured
* Fixed an issue where renaming workflows would not update until the page was refreshed
* Fixed an issue where binding HTTP/SAP request step URL attribute would appear as blank in text field
* Fixed alignment issues with multiple binding fields in a row in workflow step config editor layouts
* Fixed an issue where clicking on the + button in the workflow builder would not re-open the Toolbox
* Fixed an issue where Override Payload steps were injecting incorrect data in nested objects
* Fixed issues with triggerWorkflow API request when attempting to trigger/test newly created workflows or workflows with no executable steps
* Fixed an issue where users could not activate workflows when using Basic or Bearer Token authentication in HTTP Request steps

### 2.3.0 (20-03-2025)

#### 🚀 New Features

* **Global Datastores:** Create & manage shared data objects with Global Datastores which can be accessed across all your organization Looply workflows.
* **Workflow Environment Variables:** Define environment variable profiles within your workflows to inject environment-specific data in your processes at runtime.

#### 🛠️ Improvements

* Enhanced organization process ID usage when triggering or resuming workflow executions via developer API
* Improved the workflow step binding selection menu + added better support for nested object attribute binding

#### 🪲 Bug Fixes

* Fixed a bug where users could not version up scheduled workflows in paused status
* Fixed a bug where multiple rule conditions were not working as intended in Advanced Conditional steps
* Fixed an issue where workflows would crash & fail when workflow step bindings were undefined
* Fixed an issue where undefined workflow bindings were falling back to the invalid binding string instead of undefined value
* Fixed issues with the workflow step binding selection menu crashing when handling undefined values

### 2.2.2 (17-03-2025)

#### 🛠️ Improvements

* **Workflow Execution Storage:** Improved our process of storing workflow execution history logs for greater user control.

#### 🪲 Bug Fixes

* General bug fixes & improvements

### 2.2.1 (17-01-2025)

#### 🚀 New Features

* **Account Usage:** View your Looply organization usage data directly from the Looply dashboard with our new Account Usage page.
* **Billing Plans:** Stay up to date with your Looply subscription and monitor your quota allowance in real-time with Looply billing plans.

#### 🛠️ Improvements

* **Workflow History - Warning Logs:** Improved workflow history to highlight any warning logs thrown by your workflows regardless of success/failure.
* **Memory limit:** Increased Lambda memory limit to enable parallel processing to cater to concurrent workflow executions.
* **Debounce handling:** Enhanced debounce handling mechanism to manage rapid, multiple user actions
* **Error handling:** Improved error handling across Looply workflow to cascade runtime errors to developer.
* **Email notifications:** Organisational administrator will be notified when account quota is hit.
* **Error notifications:** Improved error reporting for workflow step failures.

#### 🪲 Bug Fixes

* Fixed a bug where connecting/disconnecting an integration did not correctly close the sidebar.
* Fixed a bug to handle user actions from android operating system.
* Fixed an  issue where updateAdaptiveCard was assuming actioned status if new card contained no action buttons.
* Fixed an issue with multiple recipients in adaptive card step, where the workflow step was failing despite having the 'Continue if action fails' flag set.

### 2.1.0 (29-07-2024)

#### 🚀 New Features

* **Advanced Conditional Steps:** New Advanced Conditional steps are now available in the workflow builder to support building complex if-else logic statements with multiple branches.
* **Workflow Error Notifications:** Workflows can now be configured to send notifications via email or webhook in the event of an error occurring during execution. Emails can be setup to be sent to one or more recipients every time an error occurs, or report any errors in batches every hour/day/week/month. Webhook requests support custom headers and basic authentication for secure communication.&#x20;

#### 🛠️ Improvements

* **Object Data Binding:** Data binding to entire objects from payload/schema is now supported in the workflow builder.
* **Adaptive Card - Subject/From Addresses:** Users can now set a subject & from address for adaptive card notifications which will be displayed with messages within the Looply Notifications Centre.
* **Workflow Configuration Error Status:** Workflows may now receive a `Configuration Error` status if an invalid workflow design is saved during development. E.g. workflow has no obvious termination point or contains conditions with no output branches to follow. Workflows with this status cannot be executed or activated.&#x20;

#### 🪲 Bug Fixes

* Fixed an issue where viewing workflow executions after testing steps resulting in a 404 error.
* Executions of draft workflows now correctly reflect the workflow steps at the time of the execution when viewing in workflow history.
* Fixed an issue where users could not view the execution history of workflows triggered from within another workflow.
* Fixed `SchemaValidation` error when updating an app with the notifications centre enabled.
* Fixed an issue where all HTTP/SAP request hardcoded body values were being treated as string.
* Fixed a bug where the adaptive card designer preview didn't update as changes were made until refreshed.
* Fixed an issue where executing incorrectly configured workflows would result in a previous version being executed instead.&#x20;
* Fixed an issue where incorrect binding on adaptive card designer could result in the black screen of death.

### 2.0.3 (23-04-2024)

#### 🪲 Bug Fixes

* Fixed an issue where the user was unable to create a new SAP integration profile using basic authentication.

### 2.0.2 (18-04-2024)

#### 🛠️ Improvements

* New warning dialog message to provide feedback when a workflow is saved with configuration errors.
* UI improvements on various screens for better user experience on smaller screens.
* Replaced Integrations sidebar with a new overlay sidebar to prevent UI issues on smaller screens.
* Changed where workflow history is stored/read from to support increased amount of workflow execution logs.
* Updated password error message to include minimum number of required characters.

#### 🪲 Bug Fixes

* Fixed an issue where SAP HTTP Requests were silently failing validation checks.

### 2.0.1 (26-03-2024)

#### 🛠️ Improvements

* **SAP OAuth2 URL:** Updated SAP integration configuration settings to now support OAuth2 authorisation outside of the host domain. As a result of this, the OAuth2 `host URL` field has been renamed `token URL` - allowing users to provide 2 different URLs for token and authorisation.&#x20;

### 2.0.0 (29-02-2024)

#### &#x20;🚀 New Features

* **New Workflow Steps:** Await Response, Redirect, Override Payload and Terminate steps are now available for use in the workflow builder.
* Looply Workflows now support multiple loops or skipping of steps.
* **Adaptive Card - Update/Delete Notify:** Users can now select an option to notify the recipients when their adaptive card is updated or deleted within Teams.&#x20;
* **Adaptive Card - More Detail Cards:**  Users can now enable & configure an expanded detailed card to be attached to their initial adaptive card within Teams which opens in a pop-up window via a button click.&#x20;
* View specific run data within Workflow History when steps have executed multiple times.
* Workflows are now validated at the point of activation to prevent missing step data.
* **Adaptive Card Designer:** Added functionality to swap between card versions & toggle between different workflow schemas for data binding.
* **Workflow Redirect Lines**: Redirect steps now implement new smart edge functionality which prevents connections from colliding with other steps.&#x20;
* **Terminate Executions:** Users can now terminate in progress workflow executions within the Workflow Execution screen.

#### 🛠️ Improvements

* **Multiple Adaptive Card Recipients:**  Users can now enter multiple recipients when sending adaptive cards within workflows which can be manually entered or bound from previous step data.
* Adaptive Card `SEND` action steps now allow for Teams or External responses to be added independently of each other (previously required both).&#x20;
* **Workflow Adaptive Card Version Selection:** Users can now choose to select `DRAFT` card versions or select an option to always use the latest version when sending adaptive cards via workflow.&#x20;
* Workflow Adaptive Card action step responses have been changed from running horizontally to linear within workflows.
* Users can now trigger the current workflow using Trigger Workflow steps (previously not supported).
* Workflow History: list view has been changed to allow more logs and includes pagination.
* Adaptive Card Designer: users now have the option to delete specific card versions.
* General workflow builder UI improvements.

#### 🪲 Bug Fixes

* Fixed an issue where users could add workflow steps with multiple branches in the middle of 2 steps (creating conflicts).
* Fixed an issue where workflow step names were not being displayed or were incorrectly cased.
* Fixed an issue where workflow last modified date was incorrect.&#x20;
* Resolved an issue where the Adaptive Card Designer & WFB Function Code Editor themes were conflicting with each other.&#x20;

### 1.1.0 (07-11-2023)

#### 🚀 New Features

* New search feature for the User Manager Tab within the Looply App creation screen.

#### 🛠️ Improvements

* Changed the location of the final Deploy button within the Looply App creation screen.
* Improved the way Looply fetches users from your teams account.
* Increased number of users shown per page on the User Manager and Install History tabs within the Looply App creation screen.
* Disabled Help section navigation options (Not yet available).
* Restyled the description tab within the Microsoft Teams integration sidebar.

#### 🪲 Bug Fixes

* Fixed an issue where installing a Looply App to any user would not update the Install History tab.

### 1.0.0 (16-10-2023)

Initial Looply alpha version featuring authenticated Looply dashboard with adaptive card designer, workflow builder, workflow history, integrations and app creation.

#### 🚀 Features

* Dashboard 2FA authentication + ability to login with TOTP authenticators
* Integrated Microsoft Adaptive Card Designer
* Looply Workflow Builder
* View Workflow History using Looply's Execution History screen
* Looply Integrations - supporting Microsoft + SAP
* Create, customise and deploy Microsoft Teams apps
* Looply Developer API + manage/create API keys
* Create and manage organisations within Looply
* Account home dashboard with usage analytics


# Contacting Support

## Contact Us

You can get in touch with us by sending us an email at **<support@looply.ai>**.&#x20;

To help us resolve your issue as soon as possible, please include as much information as possible - including the following:&#x20;

* When the issue occurred (date & time)
* ID of the resource you're having issues with (workflow ID, integration ID, adaptive card ID etc)
* Error messages
* Username/ID


# Looply SSO

Azure


