--- title: "Actions" slug: "data-pulls" updated: 2026-03-12T22:03:27Z published: 2026-03-12T22:03:27Z canonical: "help.gladly.com/data-pulls" --- > ## Documentation Index > Fetch the complete documentation index at: https://help.gladly.com/llms.txt > Use this file to discover all available pages before exploring further. # Actions Actions in the App Platform allow you to perform operations directly from Gladly Team . ## Using Actions > [!WARNING] > Use Data Pulls for all Customer queries > > Retrieving Customer related data from external systems should generally be done via Data Pulls. Data retrieved via Actions will not be available to Agents, nor in Rules and Routing. Here are some use cases for Actions: - Order Management: cancelling an order, start a return, etc. - Subscription Management: cancel, resume, change shipping date. - Gladly Tasks - Agent-initiated actions from the "+" menu (e.g., cancel order, refund order, return line item). - Any Action configured as part of an installed App Platform app. ### Actions in the Agent Desktop "+" Menu - App Platform Actions can be exposed to Agents via the Agent Desktop "+" menu. - Each action appears as a menu item; selecting it displays an input form for gathering the data needed to execute the Action. - Unlike Lookup Adaptor actions (which are limited to Customer Profile transaction buttons), app platform "+" menu actions are not limited by action type — any action configured in an installed app platform app can be surfaced. - The form configuration uses the same format as [Lookup Adaptor action forms](https://help.gladly.com/developer-tutorials/docs/app-actions-setup#configuring-the-form-for-a-specific-action). ## Actions vs Data Pulls Actions are designed to be interactive processes that execute operations in external systems based on user input. They differ from Data Pulls in these key principles: 1. They do not have to be associated with a customer. 2. Actions can execute operations in an external system. 3. Errors encountered during an Action are returned to the client for user handling. 4. Data from actions is not persistent in Gladly. ## Folder Structure Action configuration is specified in the app's actions folder. Note that most files are optional. > [!NOTE] > Use appcfg to initialize an action > > Use [appcfg add action](https://github.com/gladly/app-platform-appcfg-cli/blob/main/docs/appcfg_add_action.md) to create an "actions/{action name}" folder containing placeholders for all relevant action configuration files. ```plaintext app/ ├── actions/ │   ├── actions_schema.graphql │   ├── _run_/ │   │   └── data/ │   │       └── [scenario_name]/ │   │           └── inputs.json │   │ │   └── [action_name]/ │       ├── config.json │       ├── headers/ │       │   ├── [header_name_1].gtpl │       │   └── ... │       │   └── [header_name_n].gtpl │       ├── request_url.gtpl │       ├── request_body.gtpl │       ├── response_transformation.gtpl │       ├── _run_/ │       │   └── data/ │       │       └── [scenario_name]/ │       │           └── inputs.json │       └── _test_/ │           └── data/ │               ├── success/ │               │   ├── inputs.json │               │   ├── expected_request_body.json │               │   ├── rawData.json │               │   └── expected_response_transformation.json │               └── [error_case]/ │                   ├── inputs.json │                   ├── expected_request_body.json │                   ├── rawData.json │                   └── expected_response_transformation.json ``` ### Key Components: - **actions_schema.graphql** – Defines the GraphQL schema for your actions - **config.json** – Configures the action, including HTTP method and content type - **headers/** – Directory containing templates for custom request headers specific to the action. - **request_url.gtpl** – Template for the URL to call - **request_body.gtpl** – Template for the request body - **response_transformation.gtpl** – Template for transforming the response - **_run_/** – Contains scenarios (subdirectories with `inputs.json`) for executing actions `appcfg run`. - **_test_/** – Contains test data and expected outputs for various scenarios ### Inputs When the Gladly App Platform executes an action, it uses Go templates (.gtpl files) to dynamically construct the necessary HTTP request components: the URL, headers, and body. Input parameters are available to Action templates, on top of the [Apps Configurations](/docs/configurations) data. The API expects the names and types specified in the input parameters of the GraphQL definition for this Action. Input parameter types can be GraphQL scalars or user-defined object types. The general best practice is to use scalar types to keep the configuration simple. The input parameters are exposed to templates via the `inputs` attribute, where each input is a corresponding attribute of the `inputs` object made available to templates. The source of the values for these inputs will be configured via the Sidekick thread UI. For example, GraphQL Action definition for canceling an order: ```graphql type Mutation {    cancelOrder(orderId: String!): CancelOrderResult! @action(name: "cancel_order") } ``` The corresponding input template data for this action would be the following: ```json {    "inputs": {        "orderId": ""    } } ``` And a request URL template that uses the parameter would look like: ```graphql https://hostname.com/cancel_order?customer_order_id={{.inputs.orderId}} ``` **The complete data for action templates is as follows** ```json {    "integration": {        "configuration": {        },        "secrets": {        }    },    "inputs": {    } } ``` ## Configuration Files ### config.json ``` [app folder]/actions/[action_name]/config.json ``` The `config.json` file contains all of the configuration information that is not template-related. Each template is instead stored in its own file for readability and testing: ```json { "httpMethod": "POST", "contentType": "some content type", } ``` | **Attribute** | **Type** | **Description** | | --- | --- | --- | | httpMethod | String | The HTTP method used to submit the request. E.g. GET, POST, PUT. | | contentType | String | The Content-Type if the request has a body. | ## Template Files Templates use the Go templating language with additional functions from the [Sprig library](https://masterminds.github.io/sprig/). ### request_url.gtpl ``` [app folder]/actions/[action_name]/request_url.gtpl ``` This template specifies the fully qualified request URL for the API call to the external system. The HTTP method used to submit the request is specified in the `config.json` file. The template can access the data outlined in the **Template Data available to URL, Header and Body templates** section above. For example, request URL template: ``` https://hostname.com/cancel_order?customer_order_id={{.inputs.orderId}} ``` ### request_body.gtpl ```plaintext [app folder]/actions/[action_name]/request_body.gtpl ``` This template specifies the body's content for HTTP methods that require a body. The content type associated with the body is specified in the `config.json` file. This template defines the request body: ```plaintext {{/*  This template builds a GraphQL mutation to ship a subscription immediately.  It uses the subscriptionId input parameter to identify which subscription to ship. */}} {{$mutation := ` mutation shipSubscriptionNow($input: ShipSubscriptionNowInput!) {  shipSubscriptionNow(input: $input) {    userErrors {      message      field    }    subscription {      id    }  } } `}} {  "query": {{toJson $mutation}},  "variables": {    "input": {      "subscriptionId": "{{.inputs.subscriptionId}}"    }  } } ``` ### **Request headers** Configuration for any required request headers specific to the Action is located in the Actions’ `headers` folder. ``` [app folder]/actions/[action_name]/headers ``` Each header is configured via a template that provides the template value. The template file for each header is named after the header. ``` [app folder]/actions/[action_name]/headers/[header name].gtpl ``` ### response_transformation.gtpl In many cases, the data returned as part of the response to an Action can contain some information irrelevant to Sidekick and/or not in an optimal format. The response transformation template is used to extract relevant data from the response and convert it into a more user-friendly format. The output of the template MUST be in JSON or XML format. ``` [app folder]/actions/[action_name]/response_transformation.gtpl ``` The response data transformation template has access to all of the data provided to the URL, body, and header templates, as well as the associated HTTP request and response data. Currently, response data formatted as JSON (application/json) or XML is supported. The response data is accessed using the `rawData` attribute of the template data. In most cases, the response template will just use the data associated with the `rawData` attribute. ```json {    "request": {        "url": "",        "method": "",        "headers": {            "request_header_name": []        },        "body": ""    },    "response": {        "status": "",        "statusCode": 200,        "headers": {            "response_header_name": []        },        "body": ""    },    "rawData": null } ``` | **Attribute** | **Type** | **Description** | | --- | --- | --- | | request.url | String | The fully qualified URL of the request. | | request.method | String | HTTP method for the request. | | request.headers | Object | Each attribute corresponds to a request header. The value associated with each header is an array of String values representing one or more header values. | | request.body | String | The raw request body text. | | response.status | String | The HTTP status of the response. | | response.statusCode | Int | The HTTP status code of the response. | | response.headers | Object | Each attribute corresponds to a response header. The value associated with each header is an array of String values representing one or more header values. | | response.body | String | The raw response text. | | rawData | Object | The object representation of the JSON or XML response data. | See the accompanying golang template documentation for the syntax of accessing template data. The entire response template data looks as follows: ```json {    "integration": {        "configuration": {        },        "secrets": {        }    },    "inputs": {    },    "request": {        "url": "",        "method": "",        "headers": {            "request_header_name": []        },        "body": ""    },    "response": {        "status": "",        "statusCode": 200,        "headers": {            "response_header_name": []        },        "body": ""    },    "rawData": null } ``` For example, hypothetical response data: ```json {    "orderId": "12345",    "status": "cancelled",    "refundIssued": true,    "refundAmount": "3.99",    "refundCurrency": "USD" } ``` Response transformation template (output MUST be in JSON or XML format): ```json {{- if .rawData.refundIssued }} {    "amount": "{{.rawData.refundAmount}}",    "currency": "{{.rawData.refundCurrency}}" } {{- else -}} {{- /* return null when a refund has not been issued */ -}} null {{- end -}} ``` The resulting data format will be defined in the GraphQL schema and associated with the corresponding Action: ```graphql type Refund {    amount: Currency    currency: String } ``` The field names and types defined in the GraphQL schema **MUST** match those in the JSON generated by the template. ## GraphQL Schema The inputs and outputs associated with each configured Action are defined using a single GraphQL schema. ``` [app folder]/actions/actions_schema.graphql ``` Actions that modify (add, update, delete) data in the external system should be defined under the GraphQL `Mutation` type and Actions that retrieve data under the `Query` type. Each Action defined in the GraphQL schema is associated with its corresponding Action configuration via the `@action` directive. The `name` attribute specified in the `@action` directive corresponds to the folder name (Action name) in which the configuration for the corresponding Action resides. `@action` directive definition ```graphql # The name argument is the action name. directive @action(name: String!) on FIELD_DEFINITION ``` For example, using the above sample `cancel_order` Action: ```graphql # The format of the Refund return type corresponds to the output # generated by the response_transformation.gtpl template. type Refund {    amount: Currency    currency: String } type Mutation {    # Input parameters defined here are exposed via the    # "inputs" attribute of the data passed to the template.    # Each "inputs" child attribute is named after the    # corresponding GraphQL input parameter. I.e. a "inputs"    # attribute referenced in any of the cancel_order templates    # will match one of the input parameters defined here.    #    # cancelOrder is associated with the cancel_order action    # configuration via an @action directive. The action    # configuration is located in a subfolder named after the    # cancel_order @action directive "name" value.        """        Cancel an order using the order ID        """    cancelOrder(orderId: String!): Refund @action(name: "cancel_order") } ``` Gladly supports the standard GraphQL data types (Int, Float, String, Boolean, ID) and the following additional types. | **Data Type** | **Format** | **Description** | | --- | --- | --- | | DateTime | String value in ISO8601 date format | DateTime fields can be used in conditionals and can be manipulated. | | Currency | String representation of currency value (real number) | Representing currency values as a string ensures no loss of precision. Gladly ensures that no precision is lost when manipulating Currency values. | ## Testing Your Actions Testing is crucial to ensure your actions work correctly. The App Platform provides tools for testing: ### Test Files The `_test_` directory contains test data for different scenarios: - **inputs.json** – The input parameters for the action - **expected_request_body.json** – The expected request body after template processing - **rawData.json** – The simulated response from the external system - **expected_response_transformation.json** – The expected output after transformation It is best practice for each action to have make commands that run the action as well as its GraphQL query against the external system, for example, for our `shipNow` action would look something like: ```plaintext # Run shipNow mutation ship-now-graphql: appcfg run action-graphql -q shipNow -s '{"apiToken": "$(API_TOKEN)"}' -i '{"subscriptionId": "1e671650-4876-4egg-a0b7-130f355281ac"}' -r $(MAKEFILE_DIR) # Run shipNow action using data in `success` folder ship-now: appcfg run action shipNow -s '{"apiToken": "$(API_TOKEN)"}' -d success -r $(MAKEFILE_DIR) ``` ### Test Data Structure Your test data should closely mirror the actual data you expect to receive. Ideally, you should obtain your test data by calling the APIs directly. ```json // inputs.json {  "subscriptionId": "sub_123456789" } // expected_request_body.json {  "query": "mutation shipSubscriptionNow($input: ShipSubscriptionNowInput!) {\\\\n  shipSubscriptionNow(input: $input) {\\\\n    userErrors {\\\\n      message\\\\n      field\\\\n    }\\\\n    subscription {\\\\n      id\\\\n    }\\\\n  }\\\\n}\\\\n",  "variables": {    "input": {      "subscriptionId": "sub_123456789"    }  } } // rawData.json (success case) {  "data": {    "shipSubscriptionNow": {      "userErrors": [],      "subscription": {        "id": "sub_123456789"      }    }  } } // expected_response_transformation.json (success case) {  "message": "Subscription shipped successfully",  "ok": true } ``` ## Error Handling Actions have specific error-handling requirements: 1. **Client Interaction** – Since actions are user-initiated, errors are returned to the client. 2. **Mirror External Systems's Error Response** – To aid debugging return errors in the same structure as received from external systems. Simplify the amount of data returned. 3. **Actionable Errors** – Return clear, actionable error messages that help the user understand what happened. 4. **Error Scenarios** – Test common error scenarios (not found, permission denied, etc.). Example error handling: ```plaintext {{if .rawData.data.shipSubscriptionNow.userErrors}}  {{$errors := .rawData.data.shipSubscriptionNow.userErrors}}  {{if gt (len $errors) 0}}    {      "message": "Failed to ship subscription: {{(index $errors 0).message}}",      "ok": false    }  {{end}} {{else}} {{- toJson .rawData.data.shipSubscriptionNow }} {{end}} ``` ## Best Practices To make apps that are easy to maintain and provide a great experience, adhere to the following best practices: #### Action vs Data Pulls Before you decide to write an action, consider that actions are currently only available in Sidekick, and any data fetched with an action will not be available in other parts of Gladly (e.g., Rules or Customer Profile page). Values returned by Queries and Mutations (e.g., the Refund from cancelOrder above) become available memories in Sidekick Thread Builder. Consider this when deciding what response data to expose via the `response_transformation.gtpl` template. #### Action Names and Inputs Action names will be displayed as they are named in the GraphQL file. Action descriptions shown in the UI will be taken from the descriptions associated with each field in the GraphQL schema. Choose action and input parameter names that are self-explanatory and descriptive. Add comments based on the external systems documentation. The comments should be readable by a non-technical person. They should describe the outcome of the action in the external system. Values returned by Queries and Mutations (e.g., the Refund from cancelOrder above) become available memories in Sidekick Thread Builder. Consider this when deciding what response data to expose via the response_transformation.gtpl template. Use flat input structures to keep the configuration simple. ### Template design Check for required inputs before processing. App Platform uses templates as they are easily readable to a human. When designing templates, consider their readability and maintenance. Consider what happens if data is missing or in an unexpected format. Keep field names and structure close to the original API for easier debugging. ### Experience Design 1. **User-Centric Design** – Design actions based on the user experience you want to provide. 2. **Clear Success/Failure Indicators** – Make it obvious whether an action succeeded or failed. 3. **Actionable Errors** – Pass on any errors that are actionable by the end user to the client. Use `fail` for any unexpected errors. 4. **Consider the Workflow** – Consider where the action fits the user workflow. For example, some tasks will require the implementation of multiple actions, e.g. `cancelOrder` and `checkCancelStatus`. ## Iterative Development Process Following an iterative process can help ensure your actions work correctly: 1. **Plan & Setup** – Define the use case, review API docs, identify inputs/outputs, and document the action. 2. **Create Initial Files** – Use `appcfg add action` to generate the basic structure. 3. **Define GraphQL Schema** – Create output types and action definitions in `actions_schema.graphql`. 4. **Implement Basic Request** – Build initial request URL and body templates. 5. **Test & Refine** – Run the action, analyze the output, and refine the implementation. 6. **Add Test Cases** – Create test cases for success and error scenarios. 7. **Finalize & Document** – Update the README, refine error handling, and complete documentation.