# GraphQL API

## Overview

As of version 7.0.0, WorkflowGen features the new [GraphQL](http://graphql.org/learn/) API, which is a modern solution to create process-driven solutions such as mobile apps, web apps, and microservices that require a powerful workflow and BPM engine.

The WorkflowGen GraphQL API is a Node.js application that runs in IIS using [iisnode](https://github.com/Azure/iisnode). It enables a high level of customization such as extending the GraphQL schema with custom types, queries or operations, or implementing new authentication methods.

## About GraphQL

From the [GraphQL](http://graphql.org/) website presentation:

> "GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools."

GraphQL is a [production-ready](http://graphql.org/blog/production-ready/) and an open source technology created by Facebook. In September 2016, GitHub [announced](https://githubengineering.com/the-github-graphql-api/) its GraphQL API.

> "We’ve often heard that our REST API was an inspiration for other companies; countless tutorials refer to our endpoints. Today, we’re excited to announce our biggest change to the API since we snubbed XML in favor of JSON: we’re making the GitHub API available through GraphQL."

GraphQL is a modern API solution for React, React Native, Angular 2, and Vue based applications.

Many GraphQL tutorials are available, it's available in many languages, and it has a large [community](https://github.com/chentsulin/awesome-graphql).

## Technical requirements

In addition to the standard WorkflowGen installation, the following components are required:

* [Node.js v18.20.7 LTS](https://nodejs.org/download/release/v18.20.7/)
* [iisnode](https://github.com/Azure/iisnode/releases/tag/v0.2.21)
* [IIS URL Rewrite](https://www.iis.net/downloads/microsoft/url-rewrite)
* [Visual C++ Redistributable](https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads)\
  ✏️ **Note:** This library is required if you encounter the error `The specified module could not be found` regarding the `edge` and `edge-js` libraries when accessing the `/wfgen/graphql`, `/wfgen/hooks`, or `/wfgen/scim` web apps.&#x20;

For information on the installation procedure, see the [WorkflowGen Technical Guide](https://docs.advantys.com/workflowgen-technical-reference-guide/).

## Endpoints

The following endpoints are available:

* GraphQL API: `http://localhost/wfgen/graphql`
* GraphiQL IDE: `http://localhost/wfgen/graphql`
* GraphQL Schema (definition language): `http://localhost/wfgen/graphql/schema`

The HTTP GET method is supported on queries only. The HTTP POST method is supported on queries and operations.

## HTTP usage

[Express-graphql](https://github.com/graphql/express-graphql) is used to serve the GraphQL HTTP queries:

> GraphQL will first look for each parameter in the URL's query-string: `/graphql?query=query+getUser($id:ID){user(id:$id){name}}&variables={"id":"4"}` If not found in the query-string, it will look in the POST request body. If the POST body has not yet been parsed, express-graphql will interpret it depending on the provided `Content-Type` header:
>
> * `application/json`: the POST body will be parsed as a JSON object of parameters.
> * `application/x-www-form-urlencoded`: this POST body will be parsed as a url-encoded string of key-value pairs.
> * `application/graphql`: the POST body will be parsed as GraphQL query string, which provides the query parameter.

## Using GraphiQL IDE in a web browser

You can use [GraphiQL](https://github.com/graphql/graphiql), "a graphical interactive in-browser GraphQL IDE", to test queries and operations, and to browse the schema documentation.

{% hint style="info" %}
As of WorkflowGen version 7.15.0, the GraphiQL tool is disabled by default. You can enable it in the **GraphQL** section on the **Integration** tab in the **Configuration Panel**. You can also enable GraphiQL in the WorkflowGen `web.config` file by setting the `GraphqlGraphiqlEnabled` parameter to `Y`.
{% endhint %}

## Configuration

### Maximum query content length

The maximum GraphQL query content length can be set by configuring the `maxAllowedContentLength` property in the WorkflowGen `web.config` file. The following example shows how to configure this property as 1 MB (note that the value should always be specified in bytes, so the value in the example is 1,024,000 bytes). The default value is 30000000 bytes.

```markup
<system.webServer> 
    <security> 
        <requestFiltering> 
            <requestLimits maxAllowedContentLength="1024000" /> 
        </requestFiltering> 
    </security> 
</system.webServer>
```

### Input file allowed folders

You can configure the local or remote folder paths where files used by FILE type parameters are located using the **Input file allowed folders** setting In the **GraphQL** section on the Configuration Panel **Integration** tab. (Alternately, you can add the folder names separated by commas to the `GraphqlInputFileAllowedFolders` parameter in the WorkflowGen `web.config` file.)

{% hint style="info" %}
When using file uploads, you don't need to include the original file folder.
{% endhint %}

To disallow input file allowed folders, leave this field empty. To allow certain folders only, enter comma-separated values according to the table below:

| **Value**                    | **Description**                                                                                                                                                                                                                                                    |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Empty                        | No folders allowed                                                                                                                                                                                                                                                 |
| `*`                          | All folders allowed                                                                                                                                                                                                                                                |
| `c:\*`                       | All folders on drive `c:`                                                                                                                                                                                                                                          |
| `c:\Inetpub\*`               | All subfolders in a specific folder                                                                                                                                                                                                                                |
| `c:\Inetpub\folder*`         | <p>All <code>c:\Inetpub</code> folders whose names start with <code>folder</code> <br><br>📌 <strong>Examples:</strong></p><ul><li><code>c:\Inetpub\folder1</code> </li><li><code>c:\Inetpub\folder1\abc</code> </li><li><code>c:\Inetpub\folder2</code></li></ul> |
| `c:\Inetpub\folder2\abc\def` | Specific folder only                                                                                                                                                                                                                                               |

### **Input file allowed HTTP URLs**

You can configure allowed HTTP URLs for input files using the **Input file allowed HTTP URLs** setting in the **GraphQL** section on the Configuration Panel **Integration** tab.

To disallow file uploads using HTTP and/or HTTPS URLs, leave the field empty. To allow certain URLs only, enter comma-separated values according to the table below:

| **Value**                         | **Description**                                                                                                                                                                                                                                                                            |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Empty                             | No HTTP or HTTPS URLs allowed                                                                                                                                                                                                                                                              |
| `*`                               | All HTTP and HTTPS URLs allowed                                                                                                                                                                                                                                                            |
| `https://*`                       | HTTPS URLs only                                                                                                                                                                                                                                                                            |
| `http://*`                        | HTTP URLs only                                                                                                                                                                                                                                                                             |
| `http://mydomain/*`               | HTTP from a specific domain only                                                                                                                                                                                                                                                           |
| `http://mydomain/folder/*`        | HTTP from a specific folder only                                                                                                                                                                                                                                                           |
| `http://mydomain/folder*`         | <p>All files and folders whose names start with <code>folder</code><br><br>📌 <strong>Examples:</strong></p><ul><li><code><http://mydomain/folderfile.jpg></code></li><li><code><http://mydomain/folder/file.jpg></code></li><li><code><http://mydomain/folder2/file.jpg></code></li></ul> |
| `http://mydomain/folder/file.jpg` | Specific file only                                                                                                                                                                                                                                                                         |

### Maximum input file size

In the **GraphQL** section on the **Integration** tab in the Administration Module **Configuration Panel**, enter the maximum input file size in kilobytes in the **Maximum input file size (kB)** field.

Alternately, you can set the maximum input file content size in kilobytes as the value of the `GraphqlMaxInputFileSize` parameter in the WorkflowGen `web.config` file.

### Maximum input file content size

When working with FILE type parameters content encoded in base64, you must enter the maximum input file content size in kilobytes in the **Maximum input file content size (kB)** field in the **GraphQL** section on the **Integration** tab in the Administration Module **Configuration Panel**.

Alternately, you can set the maximum input file content size in kilobytes as the value of the `GraphqlMaxInputFileContentSize` parameter in the WorkflowGen `web.config` file.

{% hint style="info" %}
FILE type data content is only recommended for small files under 1 MB.
{% endhint %}

### GraphQL API key

As of version 8.1.3, if your WorkflowGen and GraphQL API are secured using the **WorkflowGen applicative** or **OpenID Connect** authentications, you can alternatively access the GraphQL schema (i.e. `/wfgen/graphql/schema`) and the [`Introspection`](https://graphql.org/learn/introspection/) queries using a GraphQL API key header (`x-wfgen-graphql-api-key`) defined in the HTTP request, without an authentication header.

Add or define the following configuration parameters to the main `\wfgen\web.config`:

* `<add key="GraphqlApiKeyEnabled" value="N" />`: Set to `Y` in order to enable the GraphQL API key feature.<br>
* `<add key="GraphqlApiKey" value="[YOUR_API_KEY]" />`: The value sent in the `x-wfgen-graphql-api-key` header must match this key. We recommend using a long and unique key string such as a GUID for example.

### Performance tuning

WorkflowGen is installed with the following default GraphQL settings (located under `iisnode` in `\wfgen\graphql\web.config`):

```
nodeProcessCountPerApplication="0"
maxConcurrentRequestsPerProcess="1024"
```

The value of the `nodeProcessCountPerApplication` setting is set to `0` by default for the best performance in Node.js applications. This creates one node process based on the number of virtual processors that are configured. You can change this value at any time to a custom number of node processes; for example, `nodeProcessCountPerApplication=2` will create two node processes independently of the number of virtual processors.

You can also optimize performance if needed by adjusting the `maxConcurrentRequestsPerProcess` value based on the number of potential concurrent users and requests.

For more information, see the [Best practices and troubleshooting guide for node applications on Azure Web Apps](https://docs.microsoft.com/en-us/azure/app-service/app-service-web-nodejs-best-practices-and-troubleshoot-guide#nodeprocesscountperapplication) Microsoft article.

### Cross-origin resource sharing (CORS)

In order to allow a client application (such as front-end JavaScript code from an outside domain) to access and request data from the API, you must enable and configure the [Cross-origin resource sharing (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) settings in the WorkflowGen GraphQL API module. To do this:

1. Install the [IIS CORS Module](https://www.iis.net/downloads/microsoft/iis-cors-module) on the WorkflowGen web server.<br>
2. Add the `cors` node with the list of external domains and their methods and headers (where HTTP requests will be allowed) to the WorkflowGen web configuration settings (located in `\wfgen\web.config`). See some common examples below.

📌 **Example 1: Allow all origins**

```html
<configuration>
    <location path="graphql" inheritInChildApplications="false">
        <system.webServer>
            <cors enabled="true">
                <add origin="*">
                    <allowMethods>
                        <add method="GET" />
                        <add method="POST" />
                        <add method="OPTIONS" />
                        <add method="HEAD" />
                    </allowMethods>
                    <allowHeaders>
                        <add header="Accept" />
                        <add header="Origin" />
                        <add header="Authorization" />
                        <add header="Content-Type" />
                    </allowHeaders>
                </add>
            </cors>
        </system.webServer>
    </location>
</configuration>
```

📌 **Example 2: Allow specific origins**

```html
<configuration>
    <location path="graphql" inheritInChildApplications="false">
        <system.webServer>
            <cors enabled="true">
                <add origin="https://domain.b.com" allowCredentials="true">
                    <allowMethods>
                        <add method="GET" />
                        <add method="POST" />
                        <add method="OPTIONS" />
                        <add method="HEAD" />
                    </allowMethods>
                    <allowHeaders>
                        <add header="Accept" />
                        <add header="Origin" />
                        <add header="Authorization" />
                        <add header="Content-Type" />
                    </allowHeaders>
                </add>
                <add origin="https://domain.c.com" allowCredentials="true">
                    <allowMethods>
                        <add method="GET" />
                        <add method="POST" />
                        <add method="OPTIONS" />
                        <add method="HEAD" />
                    </allowMethods>
                    <allowHeaders>
                        <add header="Accept" />
                        <add header="Origin" />
                        <add header="Authorization" />
                        <add header="Content-Type" />
                    </allowHeaders>
                </add>
            </cors>
        </system.webServer>
    </location>
</configuration>
```

For more information about the CORS configuration, see the [IIS CORS module Configuration Reference](https://docs.microsoft.com/en-us/iis/extensions/cors-module/cors-module-configuration-reference).

## Authentication

The following authentication methods are supported:

* IIS Basic
* WorkflowGen authentication
* Custom .NET authentication modules
* OpenID Connect

{% hint style="info" %}
If your WorkflowGen site is configured with Integrated Windows or Basic authentication, you must configure GraphQL with Basic authentication.
{% endhint %}

HTTPS is required to secure credentials.

For OpenID Connect providers, you need to pass the access token as a bearer token in the **Authorization** header; for example, `Authorization: Bearer <ACCESS TOKEN>`(replace`<ACCESS TOKEN>` with your access token).

The GraphQL Node.js app code inside the `\wfgen\graphql` folder can also be customized to accommodate many other authentication methods (such as OAuth2, JWT, etc.) thanks to node libraries such as [Passport.js](http://www.passportjs.org/).

## System access users

Some operations (such as `UpdateRequestDataset`) require users to have system access to perform the operations. This can be configured in the **System operations allowed users** field, under **Security** on the **Integration** tab in the **Configuration Panel**.

## User impersonation

User impersonation is supported but not recommended, and should be used only when no other technical solutions are possible. (For example, OpenID Connect-based authentication methods allow you to use access tokens to perform API operations on the client and server sides without impersonation.)

System operations allowed users can impersonate another WorkflowGen user account by setting this account's username as the value of the `x-wfgen-impersonate-username` HTTP request header.

This request header can be renamed according to your naming convention. You can specify a new header name in the `GraphqlImpersonateUserNameHttpHeader` setting in the `\wfgen\web.config` file (e.g. `<add key="GraphqlImpersonateUserNameHttpHeader" value="my-custom-impersonate-username" />`).

To give or revoke system operations permissions to or from specific users, refer to the [System operations allowed users](https://docs.advantys.com/docs/admin/configuration-panel#system-operations-allowed-users-usernames-separated-by-commas) setting in the **Security** section on the Configuration Panel **Integration** tab; alternately, you can edit the `ProcessesRuntimeWebServiceAllowedUsers` setting in the `\wfgen\web.config` file.

## Delegation mode

Some GraphQL queries and operations can be executed on behalf of another user. This is possible when a user has created a delegation in WorkflowGen. The delegatee has to specify the user ID of his delegator in the `onBehalfOf` argument.

List of actions to do on by the delegatee on behalf of the delegator with the user ID `VXNlcjoy`:

```
{
  viewer {
    actions(filter: {as: ASSIGNEE, status: OPEN}, onBehalfOf:"VXNlcjoy"}) {
      totalCount
      hasNextPage
      hasPreviousPage
      items {
        request {
          number
          description
        }
        number
        name
        description
        limit
        launchUrl
      }
    }
  }
}
```

When the `onBehalfOf` argument is set, it is propagated implicitly to the all the sub-queries and fields until a User type is used.

## Global identifiers

Each GraphQL type has an `id: ID!` field. This ID is global and is unique for all WorkflowGen objects.

You can use the `node(id:ID!)` query to retrieve a WorkflowGen object by its ID.

```
{
  node(id: "UHJvY2VzczoxNQ==") {
    id
    ... on Request {
      number
      requester {
        lastName
      }
    }
    ... on Action {
      limit
      assignee {
        id
        company
      }
    }
    ... on User {
      userName
      email
    }
  }
}
```

## GraphQL queries

You can copy/paste these queries directly in the GraphiQL IDE. See the [Using GraphiQL IDE in a web browser](#using-graphiql-ide-in-a-web-browser) section above for more information.

### Using curl

```
curl -X POST http://localhost/wfgen/graphql -H "Content-Type: application/x-www-form-urlencoded" -d "query={ viewer { userName lastName firstName email } }"
```

And the result is:

```
{
  "data": {
    "viewer": {
      "userName": "johndoe",
      "lastName": "Doe",
      "firstName": "John",
      "email": "john.doe@acme.com"
    }
  }
}
```

### Viewer basic info (the authenticated user)

```
{
  viewer {
    userName
    lastName
    firstName
    email
  }
}
```

### My actions to do

```
{
  viewer {
    actions(filter: {as: ASSIGNEE, status: OPEN}) {
      totalCount
      hasNextPage
      hasPreviousPage
      items {
        request {
          number
          description
        }
        number
        name
        description
        limit
        launchUrl
      }
    }
  }
}
```

### Fetch a request by its number

```
{
  request(number: 273) {
    description
    requester {
      lastName
      userName
      company
    }
    process {
      name
      version
    }
  }
}
```

## Request mutations

### Create a new request

{% hint style="info" %}
To create a new request from the GraphQL API, make sure that sub-process mode is enabled with public access on the target process. (See the [Process form](https://docs.advantys.com/workflowgen-administration-module-reference-guide/process-definition#process-form) section in the [WorkflowGen Administration Guide](https://docs.advantys.com/workflowgen-administration-module-reference-guide/) for more information.)
{% endhint %}

#### Request payload:

```
mutation {
  createRequest(input: {
    processName: "2_LEVELS_APPROVAL", 
    processVersion: 1
  }) {
    request {
      id
      name
      number
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "createRequest": {
      "request": {
        "id": "UmVxdWVzdDoxNQ==",
        "name": "2_LEVELS_APPROVAL #15",
        "number": 15
      }
    }
  }
}
```

A parameter's array can be included in the `createRequest` operation payload. Be aware that a data with the same name and data type must previously exist in the process for each parameter in the array to store the parameter's value. The following example shows how to send parameters corresponding to the four supported data types (TEXT, NUMERIC, DATETIME, and FILE).

#### Request payload:

```
mutation {
  createRequest(input: {processName: "SR", processVersion: 1, parameters: [{name: "TEXT", textValue: "My text parameter"}, {name: "NUMERIC", numericValue: 5}, {name: "DATE", dateTimeValue: "2017-02-23T20:46:00Z"}, {name: "FILE", fileValue: {name: "TestFile.txt", contentType: "text/plain", size: 616, url: "file:///c:/TestFile.txt", updatedAt: "2017-02-21T15:06:38Z"}}]}) {
    request {
      id
      name
      number
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "createRequest": {
      "request": {
        "id": "UmVxdWVzdDoxNg==",
        "name": "2_LEVELS_APPROVAL #16",
        "number": 16
      }
    }
  }
}
```

For more information on FILE parameter manipulations when sent within GraphQL payloads, see the [File upload](#file-upload) section.

### Update request dataset

A request dataset context can be updated by adding a parameter array. In this case a request number or a request ID should be provided.

#### Request payload:

```
mutation {
  updateRequestDataset(input: {
    number: 22, 
    parameters: {
      name: "TEXT", 
      textValue: "My text parameter"
    }
  }) {
    dataset {
      items {
        name
        textValue
      }
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "updateRequestDataset": {
      "dataset": {
        "items": [
          {
            "name": "TEXT",
            "textValue": "My text parameter"
          }
        ]
      }
    }
  }
}
```

### Cancel a request

You can cancel a request by using the request number or the request ID.

#### Request payload:

```
mutation {
  cancelRequest(input: {
    number: 15
  }) {
    request {
      id
      name
      number
      status
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "cancelRequest": {
      "request": {
        "id": "UmVxdWVzdDoxNQ==",
        "name": "SR #15",
        "number": 15,
        "status": "CLOSED"
      }
    }
  }
}
```

### Delete a request

A request can be deleted by using the request number or the request ID.

#### Request payloads:

```
mutation {
  deleteRequest(input: {
    number: 15
  }) {
    clientMutationId
  }
}
```

```
mutation {
  deleteRequest(input: {
    id: "UmVxdWVzdDoxNQ=="
  }) {
    clientMutationId
  }
}
```

#### Response payload:

```
{
  "data": {
    "deleteRequest": {
      "clientMutationId": null
    }
  }
}
```

## Action mutations

### Complete an action

To complete an action, provide the request number and the action number, or the action ID.

#### Request payload:

```
mutation {
  completeAction(input: {
    requestNumber: 16, 
    number: 1
  }) {
    action {
      id
      status
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "completeAction": {
      "action": {
        "id": "QWN0aW9uOjE2LS0tMQ==",
        "status": "CLOSED"
      }
    }
  }
}
```

To complete an action, a parameter array can be included in the request payload arguments.

#### Request payload:

```
mutation {
  completeAction(input: {
    requestNumber: 20, 
    number: 1, 
    parameters: [{
      name: "NEW_PARAMETER", 
      textValue: "My parameter"
    }]
  }) {
    action {
      id
      status
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "completeAction": {
      "action": {
        "id": "QWN0aW9uOjIwLS0tMQ==",
        "status": "CLOSED"
      }
    }
  }
}
```

### Complete a form action

This mutation completes a form action (EFORMASPX action with ASP.NET web form) and also updates the form archive and form data files.&#x20;

#### Request payload:

```
mutation {
  completeFormAction(input: {
    requestNumber: 20, 
    number: 1, 
    parameters: [{
      name: "NEW_PARAMETER", 
      textValue: "My parameter"
    }]
  }) {
    action {
      id
      status
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "completeFormAction": {
      "action": {
        "id": "QWN0aW9uOjIwLS0tMQ==",
        "status": "CLOSED"
      }
    }
  }
}
```

### Cancel an action

To cancel an action, provide the request number and the action number, or the action ID. The following conditions must be met:

* The viewer and the user (if they don't share the same identity, as in delegation mode) must have access to the request.<br>
* The action must be open.<br>
* The action must have a cancel or default exception defined in the transition.<br>
* The viewer is:<br>
  * an administrator or process folder manager\
    \
    **OR**<br>
  * a supervisor with cancellation rights\
    \
    **OR**<br>
  * the action assignee.

#### Request payload:

```
mutation {
  cancelAction(input: {
    requestNumber: 21, 
    number: 1
  }) {
    action {
      id
      status
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "cancelAction": {
      "action": {
        "id": "QWN0aW9uOjIxLS0tMQ==",
        "status": "CLOSED"
      }
    }
  }
}
```

### Cancel a request's actions by name

All of the actions with the same name in a request can be cancelled at the same time by using their name in this operation payload. The following conditions are met:

* The viewer and the user (if they do not share the same identity, as in delegation mode) must have access to the request.<br>
* The action must be open.<br>
* The action must have a cancel or default exception defined in the transition.<br>
* The viewer is:<br>
  * an administrator or a process folder manager\
    \
    **OR**<br>
  * a supervisor with cancellation rights\
    \
    **OR**<br>
  * the action assignee.

#### Request payload:

```
mutation {
  cancelRequestActionsByName(input: {
    requestNumber: 21, 
    activityName: "INITIATES" 
  }) {
    action {
      id
      status
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "cancelAction": {
      "action": {
        "id": "QWN0aW9uOjIxLS0tMQ==",
        "status": "CLOSED"
      }
    }
  }
}
```

### Assign an action

To assign an action, provide the request number and the action number, or the action ID; you must also provide the `assigneeUserName` or the `assigneeId`.

#### Request payload:

```
mutation {
  assignAction(input: {
    requestNumber: 22, 
    number: 1, 
    assigneeId: "VXNlcjox"
  }) {
    action {
      id
      assignee {
        id
      }
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "assignAction": {
      "action": {
        "id": "QWN0aW9uOjIyLS0tMQ==",
        "assignee": {
          "id": "VXNlcjox"
        }
      }
    }
  }
}
```

### Cancel an action assignment

To cancel an action assignment, you should provide the request number and the action number, or the action ID.

#### Request payload:

```
mutation {
  cancelActionAssignment(input: {
    requestNumber: 22, 
    number: 1
  }) {
    action {
      id
      assignee {
        id
      }
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "cancelActionAssignment": {
      "action": {
        "id": "QWN0aW9uOjIyLS0tMQ==",
        "assignee": null
      }
    }
  }
}
```

### Start an interactive action

&#x20;An interactive action (including data locks) can be started by using the `StartInteractiveAction` mutation. To perform this mutation, provide the **either** request number and the action number, the request number and the activity name, **or** the action ID.&#x20;

The following conditions must be met:

* The viewer and the user (if they don't share the same identity, as in delegation mode) must have access to the request.<br>
* The action must be open.<br>
* The action type must be human.<br>
* The user must have the rights to to complete the action.

#### Request payload:

```
mutation {
  startInteractiveAction(input: {requestNumber: 73, number: 2}) {
    action {
      status
      subStatus
      interactiveParameters {
        totalCount
        items {
          name
          direction
          hasValue
          type
          textValue
          numericValue
          dateTimeValue
          fileValue {
            url
            blobUrl
            name
          }
        }
      }
    }
  }
}
```

An array of interactive parameters is required to complete the action; it can be returned by using the `action.interactiveParameters` query.

#### Response payload:

```
{
  "data": {
    "startInteractiveAction": {
      "action": {
        "status": "OPEN",
        "subStatus": "RUNNING",
        "interactiveParameters": {
          "totalCount": 14,
          "items": [
            {
              "name": "CURRENT_ACTION",
              "direction": "IN",
              "hasValue": true,
              "type": "TEXT",
              "textValue": "VALIDATES",
              "numericValue": null,
              "dateTimeValue": null,
              "fileValue": null
            },
            {
              "name": "CURRENT_REQUEST",
              "direction": "IN",
              "hasValue": true,
              "type": "NUMERIC",
              "textValue": null,
              "numericValue": 73,
              "dateTimeValue": null,
              "fileValue": null
            },
            {
              "name": "FORM_ARCHIVE",
              "direction": "OUT",
              "hasValue": false,
              "type": "FILE",
              "textValue": null,
              "numericValue": null,
              "dateTimeValue": null,
              "fileValue": null
            },
            {
              "name": "FORM_DATA",
              "direction": "INOUT",
              "hasValue": false,
              "type": "FILE",
              "textValue": null,
              "numericValue": null,
              "dateTimeValue": null,
              "fileValue": null
            },
            {
              "name": "FORM_DRAFT",
              "direction": "OUT",
              "hasValue": false,
              "type": "TEXT",
              "textValue": null,
              "numericValue": null,
              "dateTimeValue": null,
              "fileValue": null
            },
            {
              "name": "FORM_FIELDS_HIDDEN",
              "direction": "IN",
              "hasValue": true,
              "type": "TEXT",
              "textValue": "LEVEL2",
              "numericValue": null,
              "dateTimeValue": null,
              "fileValue": null
            },
            {
              "name": "FORM_FIELDS_READONLY",
              "direction": "IN",
              "hasValue": true,
              "type": "TEXT",
              "textValue": "REQUEST1_*,REQUEST_*",
              "numericValue": null,
              "dateTimeValue": null,
              "fileValue": null
            },
            {
              "name": "FORM_FIELDS_REQUIRED",
              "direction": "IN",
              "hasValue": true,
              "type": "TEXT",
              "textValue": "LEVEL1_DECISION",
              "numericValue": null,
              "dateTimeValue": null,
              "fileValue": null
            },
            {
              "name": "FORM_URL",
              "direction": "IN",
              "hasValue": true,
              "type": "TEXT",
              "textValue": "./wfapps/webforms/2_LEVELS_APPROVAL/V1/Default.aspx",
              "numericValue": null,
              "dateTimeValue": null,
              "fileValue": null
            },
            {
              "name": "LEVEL1_DATE",
              "direction": "IN",
              "hasValue": true,
              "type": "DATETIME",
              "textValue": null,
              "numericValue": null,
              "dateTimeValue": "2021-02-08T19:21:51.033Z",
              "fileValue": null
            },
            {
              "name": "LEVEL1_DECISION",
              "direction": "INOUT",
              "hasValue": true,
              "type": "TEXT",
              "textValue": "default value",
              "numericValue": null,
              "dateTimeValue": null,
              "fileValue": null
            },
            {
              "name": "LEVEL1_FIRSTNAME",
              "direction": "IN",
              "hasValue": true,
              "type": "TEXT",
              "textValue": "WorkflowGen",
              "numericValue": null,
              "dateTimeValue": null,
              "fileValue": null
            },
            {
              "name": "LEVEL1_LASTNAME",
              "direction": "IN",
              "hasValue": true,
              "type": "TEXT",
              "textValue": "Administrator",
              "numericValue": null,
              "dateTimeValue": null,
              "fileValue": null
            },
            {
              "name": "REQUEST_ATTACHMENT",
              "direction": "INOUT",
              "hasValue": false,
              "type": "FILE",
              "textValue": null,
              "numericValue": null,
              "dateTimeValue": null,
              "fileValue": null
            }
          ]
        }
      }
    }
  }
}
```

## End-user mutations

### Create a favorite

GraphQL lets users add processes and views to their favorites lists using the process or view IDs.

#### Request payload:

```
mutation {
  createFavorite(input: {
    itemId: "UHJvY2Vzczoy"
  }) {
    favorite {
      id
      type
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "createFavorite": {
      "favorite": {
        "id": "RmF2b3JpdGU6MQ==",
        "type": "PROCESS"
      }
    }
  }
}
```

This code will create a favorite using the process or view description, but adding a custom description is also possible, as shown in the following example:

#### Request payload:

```
mutation {
  createFavorite(input: {
    itemId: "UHJvY2Vzczoy", 
    description: "My custom description"
  }) {
    favorite {
      id
      type
      description
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "createFavorite": {
      "favorite": {
        "id": "RmF2b3JpdGU6MQ==",
        "type": "PROCESS",
        "description": "My custom description"
      }
    }
  }
}
```

### Update a favorite

GraphQL lets users update an existing favorite process or view by using its favorite ID.

#### Request payload:

```
mutation {
  updateFavorite(input: {
    id: "RmF2b3JpdGU6MQ==",
    description: "Updated description"
  }) {
    favorite {
      id
      type
      description
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "updateFavorite": {
      "favorite": {
        "id": "RmF2b3JpdGU6MQ==",
        "type": "PROCESS",
        "description": "Updated description"
      }
    }
  }
}
```

### Delete a favorite

GraphQL lets users delete an existing favorite process or view from their favorites list by using its favorites ID.

#### Request payload:

```
mutation {
  deleteFavorite(input: {
    id: "RmF2b3JpdGU6MQ=="
  }) {
    clientMutationId
  }
}
```

#### Response payload:

```
{
  "data": {
    "deleteFavorite": {
      "clientMutationId": null
    }
  }
}
```

### Add a comment

GraphQL lets users add comments to requests.

#### Request payload:

```
mutation {
  addComment(input: {
    subjectId: "UmVxdWVzdDoyMg==", 
    message: "This is my message"
  }) {
    comment {
      subject {
        id
      }
      message
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "addComment": {
      "comment": {
        "subject": {
          "id": "UmVxdWVzdDoyMg=="
        },
        "message": "This is my message"
      }
    }
  }
}
```

### Update a comment

Users can update comments if they are administrators, process manager of the process or process supervisor, or standard users who are members of the process participants, have write permissions, and are the comment author.

#### Request payload:

```
mutation {
  updateComment(input: {
    id: "Q29tbWVudDoyMi0tLTE=",
    message: "This is my updated message"
  }) {
    comment {
       message
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "updateComment": {
      "comment": {
        "message": "This is my updated message"
      }
    }
  }
}
```

### Remove a comment

GraphQL lets users remove comments from requests.

#### Request payload:

```
mutation {
  removeComment(input: {
    id: "Q29tbWVudDoyMi0tLTE="
  }) {
    clientMutationId
  }
}
```

#### Response payload:

```
{
  "data": {
    "removeComment": {
      "clientMutationId": null
    }
  }
}
```

## Delegation mutations

### Create a delegation

This mutation requires that the portal delegation feature be enabled. Standard users can only create delegations for themselves; only administrators can create a delegation for another user.

#### Request payload:

```
mutation {  
  createDelegation(input: {
    delegatorId: "VXNlcjox", 
    delegateeId: "VXNlcjoy", 
    processId: "UHJvY2Vzczoz", 
    participantId: "TG9jYWxQcm9jZXNzUGFydGljaXBhbnQ6OA==", 
    start: "2017-04-23T18:25:43.000Z", 
    end: "2017-11-23T18:25:43.000Z", 
    notifyDelegatee: true
  }) {    
    delegation {      
      id    
    }  
  }
}
```

#### Response payload:

```
{
  "data": {
    "createDelegation": {
      "delegation": {
        "id": "RGVsZWdhdGlvbjoxMA=="
      }
    }
  }
}
```

### Update a delegation

This mutation requires that the portal delegation feature be enabled. Standard users can only update delegations for themselves; only administrators can update a delegation for another user.

#### Request payload:

```
mutation {  
  updateDelegation(input: {
    id: "RGVsZWdhdGlvbjoxMA==", 
    start: "2018-05-01T00:00:00.000Z", 
    end: "2018-9-01T00:00:00.000Z", 
    notifyDelegatee: true
  }) {    
    delegation {      
      id    
    }  
  }
}
```

#### Response payload:

```
{
  "data": {
    "updateDelegation": {
      "delegation": {
        "id": "RGVsZWdhdGlvbjoxMA=="
      }
    }
  }
}
```

### Delete a delegation

This mutation requires that the portal delegation feature be enabled. Standard users can only delete their own delegations; only administrators can delete another user's delegations.

#### Request payload:

```
mutation {  
  deleteDelegation(input: {
    id: "RGVsZWdhdGlvbjoxMA=="
  }) {    
    clientMutationId
  }
}
```

#### Response payload:

```
{
  "data": {
    "deleteDelegation": {
      "clientMutationId": null
    }
  }
}
```

## Category mutations

### Create a category

Only administrators can create a category.

#### Request payload:

```
mutation {
  createCategory(input: {
    name: "my category", 
    description: "My category description"
  }) {
    category {
      name
      description
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "createCategory": {
      "category": {
        "name": "MY_CATEGORY",
        "description": "My category description"
      }
    }
  }
}
```

### Update a category

Only administrators can update a category.

#### Request payload:

```
mutation {
  updateCategory(input: {
    id: "Q2F0ZWdvcnk6Mg==", 
    name: "My updated category name"
  }) {
    category {
      id
      name
      description
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "updateCategory": {
      "category": {
        "id": "Q2F0ZWdvcnk6Mg==",
        "name": "MY_UPDATED_CATEGORY_NAME",
        "description": "My category description"
      }
    }
  }
}
```

### Delete a category

Only administrators can delete a category.

#### Request payload:

```
mutation {
  deleteCategory(input: {
    id: "Q2F0ZWdvcnk6Mg=="
  }) {
    clientMutationId
  }
}
```

#### Response payload:

```
{
  "data": {
    "deleteCategory": {
      "clientMutationId": null
    }
  }
}
```

## Process folder mutations

### Create a process folder

Only administrators can create a process folder.

#### Request payload:

```
mutation {
  createProcessFolder(input: {
    name: "Human Resources processes folder", 
    description: "Human Resources processes folder", 
    managerId: "R2xvYmFsUGFydGljaXBhbnQ6MQ=="
  }) {
    processFolder {
      id
      name
      description
      manager {
        id
        name
      }
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "createProcessFolder": {
      "processFolder": {
        "id": "UHJvY2Vzc0ZvbGRlcjoz",
        "name": "HUMAN_RESOURCES_PROCESSES_FOLDER",
        "description": "Human Resources processes folder",
        "manager": {
          "id": "R2xvYmFsUGFydGljaXBhbnQ6MQ==",
          "name": "DEFAULT_PROCESSMANAGER"
        }
      }
    }
  }
}
```

### Update a process folder

Only administrators and the process folder manager can update a process folder.

#### Request payload:

```
mutation {
  updateProcessFolder(input: {
    id: "UHJvY2Vzc0ZvbGRlcjoz", 
    name: "Updated processes folder"
  }) {
    processFolder {
      id
      name
      description
      manager {
        id
        name
      }
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "updateProcessFolder": {
      "processFolder": {
        "id": "UHJvY2Vzc0ZvbGRlcjoz",
        "name": "UPDATED_PROCESSES_FOLDER",
        "description": "Human Resources processes folder",
        "manager": {
          "id": "R2xvYmFsUGFydGljaXBhbnQ6MQ==",
          "name": "DEFAULT_PROCESSMANAGER"
        }
      }
    }
  }
}
```

### Delete a process folder

Only administrators can delete a process folder.

#### Request payload:

```
mutation {
  deleteProcessFolder(input: {
    id: "UHJvY2Vzc0ZvbGRlcjoz"
  }) {
    clientMutationId
  }
}
```

#### Response payload:

```
{
  "data": {
    "deleteProcessFolder": {
      "clientMutationId": null
    }
  }
}
```

## Process mutations

Process mutations are available as of WorkflowGen version 7.16.0. They can only be performed by administrators or process folder managers of the folder where the process is located.

### Create a process

The `createProcess` mutation creates a process from the process properties. The process name, description, state, and folder identifier or name are required.

#### Request payload:

```
mutation {
  createProcess(input: {
    name: "My new process", 
    description: "my new process", 
    folderName: "DEFAULT"
  }) {
    process {
      id
      name
      version
      description
      state
      folder {
        name
      }
      isSubProcess
      isBuiltInForm
      isActionDataArchived
      isDatabaseStorageForFiles
      accessLevel
      updatedAt
      updatedBy {
        userName
      }
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "createProcess": {
      "process": {
        "id": "UHJvY2Vzczoy",
        "name": "MY_NEW_PROCESS",
        "version": 1,
        "description": "my new process",
        "state": "TEST",
        "folder": {
          "name": "DEFAULT"
        },
        "isSubProcess": false,
        "isBuiltInForm": true,
        "isActionDataArchived": false,
        "isDatabaseStorageForFiles": false,
        "accessLevel": "PUBLIC",
        "updatedAt": "2019-11-08T18:23:07.577Z",
        "updatedBy": {
          "userName": "wfgen_admin"
        }
      }
    }
  }
}
```

The  `createProcess` mutation can also be used to create a new process version by passing the `fromProcessId` property in the arguments as shown below.

#### Request payload:

```
mutation {
  createProcess(input: {
    fromProcessId: "UHJvY2Vzczoy"
  }) {
    process {
      id
      name
      version
      description
      state
      folder {
        name
      }
      isSubProcess
      isBuiltInForm
      isActionDataArchived
      isDatabaseStorageForFiles
      accessLevel
      updatedAt
      updatedBy {
        userName
      }
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "createProcess": {
      "process": {
        "id": "UHJvY2Vzczoz",
        "name": "MY_NEW_PROCESS",
        "version": 2,
        "description": "my new process",
        "state": "DEV",
        "folder": {
          "name": "DEFAULT"
        },
        "isSubProcess": false,
        "isBuiltInForm": true,
        "isActionDataArchived": false,
        "isDatabaseStorageForFiles": false,
        "accessLevel": "PUBLIC",
        "updatedAt": "2019-11-08T18:27:25.190Z",
        "updatedBy": {
          "userName": "wfgen_admin"
        }
      }
    }
  }
}
```

### Create a process from an XPDL

A process can be created from its XPDL definition. The XPDL file (in `.xml` format) is uploaded by using the GraphQL [multipart file upload](#multipart-file-upload) feature as shown below.

#### curl request:

```
curl -X POST http://localhost/wfgen/graphql -H "content-type: multipart/form-data" -F "operations={ \"query\": \"mutation ($fileUpload1: Upload) { createProcessFromXpdl(input: { folderName: \\\"DEFAULT\\\"  xpdl: { upload: $fileUpload1 } }) { process { id } } }\",  \"variables\": { \"fileUpload1\": null } }" -F "map={ \"1\": [\"variables.fileUpload1\"] }" -F "1=@C:\MY_PROCESSv1.xml"
```

#### Response:

```
{
  "data": {
    "createProcessFromXpdl": {
      "process": {
        "id": "UHJvY2Vzczo0"
      }
    }
  }
} 
```

The following optional properties are available:

| **Property**                       | **Description**                                                                                                                                                                                     |
| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `addNewParticipantsAsGlobal`       | <p>Specifies whether or not to import process participants as global participants (can only be used by users with an administrator profile)<br><br><strong>Default:</strong> <code>false</code></p> |
| `addGlobalParticipantAssociations` | Specifies whether or not to import global participant associations                                                                                                                                  |
| `addSelfToParticipant`             | Specifies whether or not to add the current user to process participants                                                                                                                            |
| `useVersionNumber`                 | <p>Specifies whether or not to import the process version number from the process definition</p><p></p><p><strong>Default:</strong> <code>true</code></p>                                           |
| `name`                             | Process name                                                                                                                                                                                        |
| `state`                            | Process state                                                                                                                                                                                       |

### Update a process

The `updateProcess` mutation updates process properties from the process identifier.

#### Request payload:

```
mutation {
  updateProcess(input: {
    id: "UHJvY2Vzczoy", 
    name: "My new name"
  }) {
    process {
      id
      name
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "updateProcess": {
      "process": {
        "id": "UHJvY2Vzczoy",
        "name": "MY_NEW_NAME"
      }
    }
  }
}
```

### Update a process from an XPDL

A process can be updated from its XPDL definition. The XPDL file (in `.xml` format) is uploaded by using the GraphQL [multipart file upload](#multipart-file-upload) feature as shown below.

#### curl request:

```
curl -X POST http://localhost/wfgen/graphql -H "content-type: multipart/form-data" -F "operations={ \"query\": \"mutation ($fileUpload1: Upload) { updateProcessFromXpdl(input: {id: \\\"UHJvY2Vzczo0\\\" xpdl: { upload: $fileUpload1} }) { process { id } } }\", \"variables\": { \"fileUpload1\": null } }" -F "map={ \"1\": [\"variables.fileUpload1\"] }" -F "1=@C:\MY_UPDATED_PROCESSv1.xml"
```

#### Response:

```
{
  "data": {
    "updateProcessFromXpdl": {
      "process": {
        "id": "UHJvY2Vzczo4"
      }
    }
  }
} 
```

The following optional properties are available:

| **Property**                       | **Description**                                                                                                                                                                                     |
| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `addNewParticipantsAsGlobal`       | <p>Specifies whether or not to import process participants as global participants (can only be used by users with an administrator profile)<br><br><strong>Default:</strong> <code>false</code></p> |
| `addGlobalParticipantAssociations` | Specifies whether or not to import global participant associations                                                                                                                                  |
| `addSelfToParticipant`             | Specifies whether or not to add the current user to process participants                                                                                                                            |
| `useVersionNumber`                 | <p>Specifies whether or not to import the process version number from the process definition</p><p></p><p><strong>Default:</strong> <code>true</code></p>                                           |

### Delete a process

A process can be deleted by using the process identifier.

#### Request payload:

```
mutation {
  deleteProcess(input: {
    id: "UHJvY2Vzczoy"
  }) {
    clientMutationId
  }
}
```

#### Response payload:

```
{
  "data": {
    "deleteProcess": {
      "clientMutationId": null
    }
  }
}
```

## Participant mutations

### Create a new global participant

#### Request payload:

```
mutation {
  createGlobalParticipant(input: {
    name: "My global participant",
    description: "My global participant description",
    type: COORDINATOR,
    userIds: ["VXNlcjox", "VXNlcjoy"],
    groupIds: ["R3JvdXA6MQ=="],
    directoryIds: ["RGlyZWN0b3J5OjE="],
    coordinatorIds: ["VXNlcjoy"]
  }) {
    globalParticipant {
      id
      name
      description
      type
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "createGlobalParticipant": {
      "globalParticipant": {
        "id": "R2xvYmFsUGFydGljaXBhbnQ6MQ==",
        "name": "MY_GLOBAL_PARTICIPANT",
        "description": "My global participant description",
        "type": "COORDINATOR"
      }
    }
  }
}
```

### Update a global participant

#### Request payload:

```
mutation {
  updateGlobalParticipant(input: {
    id: "R2xvYmFsUGFydGljaXBhbnQ6MQ=="
    name: "My global participant",
    description: "My global participant description",
    type: COORDINATOR,
    userIds: ["VXNlcjox", "VXNlcjoy"],
    groupIds: ["R3JvdXA6MQ=="],
    directoryIds: ["RGlyZWN0b3J5OjE="],
    coordinatorIds: ["VXNlcjoy"]
  }) {
    globalParticipant {
      id
      name
      description
      type
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "updateGlobalParticipant": {
      "globalParticipant": {
        "id": "R2xvYmFsUGFydGljaXBhbnQ6MQ==",
        "name": "MY_GLOBAL_PARTICIPANT",
        "description": "My global participant description",
        "type": "COORDINATOR"
      }
    }
  }
}
```

### Delete a global participant

#### Request payload:

```
mutation {
  deleteGlobalParticipant(input: {
    id: "R2xvYmFsUGFydGljaXBhbnQ6MQ=="
  }) {
  	clientMutationId 
  }
}
```

#### Response payload:

```
{
  "data": {
    "deleteGlobalParticipant": {
      "clientMutationId": null
    }
  }
}
```

### Add a new process participant profile

To be used with `global participant` only.

#### Request payload:

```
mutation {
  addProcessParticipantProfile(input: {
  	role: ACTOR
    participantId: "R2xvYmFsUGFydGljaXBhbnQ6NQ==",
    processId: "UHJvY2Vzczox"
  }) {
     processParticipantProfile {
      id
      role
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "addProcessParticipantProfile": {
      "processParticipantProfile": {
        "id": "UHJvY2Vzc1BhcnRpY2lwYW50UHJvZmlsZToxLS0tMTE=",
        "role": "ACTOR"
      }
    }
  }
}
```

### Update a process participant profile

To be used with `global participant` only.

#### Request payload:

```
mutation {
  updateProcessParticipantProfile(input: {
    id: "UHJvY2Vzc1BhcnRpY2lwYW50UHJvZmlsZToxLS0tMTE=",
  	role: SUPERVISOR
  }) {
     processParticipantProfile {
      id
      role
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "updateProcessParticipantProfile": {
      "processParticipantProfile": {
        "id": "UHJvY2Vzc1BhcnRpY2lwYW50UHJvZmlsZToxLS0tMTE=",
        "role": "SUPERVISOR"
      }
    }
  }
}
```

### Remove a process participant profile

To be used with `global participant` only.

#### Request payload:

```
mutation {
  removeProcessParticipantProfile(input: {
    id: "UHJvY2Vzc1BhcnRpY2lwYW50UHJvZmlsZToxLS0tMTE="
  }) {
  	clientMutationId
  }
}
```

#### Response payload:

```
{
  "data": {
    "removeProcessParticipantProfile": {
      "clientMutationId": null
    }
  }
}
```

### Create a new local process participant

#### Request payload:

```
mutation {
  createLocalProcessParticipant(input: {
    name: "My local process participant",
    description: "My local process participant description",
    type: COORDINATOR,
    userIds: ["VXNlcjox", "VXNlcjoy"],
    groupIds: ["R3JvdXA6MQ=="],
    directoryIds: ["RGlyZWN0b3J5OjE="],
    coordinatorIds: ["VXNlcjoy"],
    processId: "UHJvY2Vzczoy"
  }) {
    localProcessParticipant {
      id
      name
      description
      type
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "createLocalProcessParticipant": {
      "localProcessParticipant": {
        "id": "TG9jYWxQcm9jZXNzUGFydGljaXBhbnQ6MTI=",
        "name": "MY_LOCAL_PROCESS_PARTICIPANT",
        "description": "My local process participant description",
        "type": "COORDINATOR"
      }
    }
  }
}
```

### Update a local process participant

#### Request payload:

```
mutation {
  updateLocalProcessParticipant(input: {
    id: "TG9jYWxQcm9jZXNzUGFydGljaXBhbnQ6MTI=",
    name: "My local process participant",
    description: "My local process participant description",
    type: COORDINATOR,
    userIds: ["VXNlcjox", "VXNlcjoy"],
    groupIds: ["R3JvdXA6MQ=="],
    directoryIds: ["RGlyZWN0b3J5OjE="],
    coordinatorIds: ["VXNlcjoy"]
  }) {
    localProcessParticipant {
      id
      name
      description
      type
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "updateLocalProcessParticipant": {
      "localProcessParticipant": {
        "id": "TG9jYWxQcm9jZXNzUGFydGljaXBhbnQ6MTI=",
        "name": "MY_LOCAL_PROCESS_PARTICIPANT",
        "description": "My local process participant description",
        "type": "COORDINATOR"
      }
    }
  }
}
```

### Delete a local process participant

#### Request payload:

```
mutation {
  deleteLocalProcessParticipant(input: {
    id: "TG9jYWxQcm9jZXNzUGFydGljaXBhbnQ6MTI="
  }) {
    clientMutationId
  }
}
```

#### Response payload:

```
{
  "data": {
    "deleteLocalProcessParticipant": {
      "clientMutationId": null
    }
  }
}
```

## Application mutations

### Create an application

Only administrators can create a workflow application.

#### Request payload:

```
mutation {
  createApplication(input: {
    name: "My assembly application", 
    description: "My assembly application description", 
    type: ASSEMBLY, 
    assemblyName: "CustomAssembly.Applications", 
    assemblyClassName: "CustomAssembly.Applications.Converters", 
    method: "Convert", 
    isDefault: false, 
    isActive: true
  }) {
    application {
      id
      name
      type
      description
      assemblyName
      assemblyClassName
      method
      isDefault
      isActive
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "createApplication": {
      "application": {
        "id": "QXBwbGljYXRpb246MzE=",
        "name": "MY_ASSEMBLY_APPLICATION",
        "type": "ASSEMBLY",
        "description": "My assembly application description",
        "assemblyName": "CustomAssembly.Applications",
        "assemblyClassName": "CustomAssembly.Applications.Converters",
        "method": "Convert",
        "isDefault": false,
        "isActive": true
      }
    }
  }
}
```

### Update an application

Only administrators can update a workflow application.

#### Request payload:

```
mutation {
  updateApplication(input: {
    id: "QXBwbGljYXRpb246MzE=", 
    name: "My updated application name"
  }) {
    application {
      id
      name
      type
      description
      assemblyName
      assemblyClassName
      method
      isDefault
      isActive
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "updateApplication": {
      "application": {
        "id": "QXBwbGljYXRpb246MzE=",
        "name": "MY_UPDATED_APPLICATION_NAME",
        "type": "ASSEMBLY",
        "description": "My assembly application description",
        "assemblyName": "CustomAssembly.Applications",
        "assemblyClassName": "CustomAssembly.Applications.Converters",
        "method": "Convert",
        "isDefault": false,
        "isActive": true
      }
    }
  }
}
```

### Delete an application

Only administrators can delete a workflow application.

#### Request payload:

```
mutation {
  deleteApplication(input: {
    id: "QXBwbGljYXRpb246MzE="
  }) {
    clientMutationId
  }
}
```

#### Response payload:

```
{
  "data": {
    "deleteApplication": {
      "clientMutationId": null
    }
  }
}
```

### Add an application parameter

Only administrators can add a workflow application parameter.

#### Request payload:

```
mutation {
  addApplicationParameter(input: {
    applicationId: "QXBwbGljYXRpb246MjA=", 
    name: "My parameter", 
    description: "My parameter description", 
    dataType: TEXT, 
    direction: IN, 
    isRequired: true, 
    isDefault: false
  }) {
    parameter {
      id
      name
      description
      dataType
      direction
    }
  }
}

```

#### Response payload:

```
{
  "data": {
    "addApplicationParameter": {
      "parameter": {
        "id": "QXBwbGljYXRpb25QYXJhbWV0ZXI6MjAtLS02",
        "name": "MY_PARAMETER",
        "description": "My parameter description",
        "dataType": "TEXT",
        "direction": "IN"
      }
    }
  }
}
```

### Remove an application parameter

Only administrators can remove a workflow application parameter if it is not associated to any activity and is not required.

#### Request payload:

```
mutation {
  removeApplicationParameter(input: {
    id: "QXBwbGljYXRpb25QYXJhbWV0ZXI6MjAtLS02"
  }) {
   clientMutationId
  }
}
```

#### Response payload:

```
{
  "data": {
    "removeApplicationParameter": {
      "clientMutationId": null
    }
  }
}
```

## Global list mutations

Global list mutations are available as of WorkflowGen version 7.17.0. They all can only be performed by administrators. A global list manager can only update a global list from an XML definition.

### Create a global list

The `createGlobalList` mutation creates a global list from the global list's properties. If no input argument is provided, the global list is created with the default values.

#### Request payload:

```
mutation {
  createGlobalList(input: {}) {
    globalList {
      id
      name
      manager {
        id
      }
      connectionName
      connectionString
      providerName
      selectCommand
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "createGlobalList": {
      "globalList": {
        "id": "R2xvYmFsTGlzdDoxMw==",
        "name": "My new list 13",
        "manager": null,
        "connectionName": null,
        "connectionString": null,
        "providerName": null,
        "selectCommand": null
      }
    }
  }
}
```

&#x20;The `CreateGlobalList` mutation can also be used to duplicate a global list by passing the `fromGlobalListId` property in the arguments:

#### Request payload:

```
mutation {
  createGlobalList(input: {
    fromGlobalListId:"R2xvYmFsTGlzdDoxMw=="
  }) {
    globalList {
      id
      name
      manager {
        id
      }
      connectionName
      connectionString
      providerName
      selectCommand
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "createGlobalList": {
      "globalList": {
        "id": "R2xvYmFsTGlzdDoxNA==",
        "name": "My new list 13 - copy 1",
        "manager": null,
        "connectionName": null,
        "connectionString": null,
        "providerName": null,
        "selectCommand": null
      }
    }
  }
}
```

### Create a global list from an XML definition

A global list can be created from its XML definition. The `.xml` file is uploaded by using the GraphQL [multipart file upload](#multipart-file-upload) feature as shown below.

#### curl request:

```
curl -X POST http://localhost/wfgen/graphql -H "content-type: multipart/form-data" -F "operations={ \"query\": \"mutation ($fileUpload1: Upload) { createGlobalListFromXmlDefinition(input: { xmlDefinition: { upload: $fileUpload1} }) { globalList { name } } }\", \"variables\": { \"fileUpload1\": null } }" -F "map={ \"1\": [\"variables.fileUpload1\"] }" -F "1=@C:\importedlist.xml"
```

#### Response:

```
{
  "data": {
    "createGlobalListFromXmlDefinition": {
      "globalList": {
        "name": "importedlist"
      }
    }
  }
} 
```

### Create a global list with databinding connections

Global list creation and update operations support databinding connections to get the list items. Two databinding modes are available: **connection name** and **connection string**.

#### Request payload (connection name mode):

```
mutation {
  createGlobalList(input: {
    connectionName: "MainDbSource", 
    selectCommand: "Select ID_USER,FIRSTNAME from USERS"
  }) {
    globalList {
      id
      name
      manager {
        id
      }
      connectionName
      connectionString
      providerName
      selectCommand
    }
  }
}
```

#### Response payload (connection name mode):

```
{
  "data": {
    "createGlobalList": {
      "globalList": {
        "id": "R2xvYmFsTGlzdDoxNg==",
        "name": "My new list 14",
        "manager": null,
        "connectionName": "MainDbSource",
        "connectionString": null,
        "providerName": null,
        "selectCommand": "Select ID_USER,FIRSTNAME from USERS"
      }
    }
  }
}
```

#### Request payload (connection string mode):

```
mutation {
  createGlobalList(input: {
    connectionString: "Data Source=localhost;Initial Catalog=MY_DB;User ID=MY_USER;Password=MyPass$;", 
    selectCommand: "Select * from USERS", 
    providerName: SYSTEM_DATA_SQLCLIENT
  }) {
    globalList {
      id
      name
      manager {
        id
      }
      connectionName
      connectionString
      providerName
      selectCommand
    }
  }
}
```

#### Response payload (connection string mode):

```
{
  "data": {
    "createGlobalList": {
      "globalList": {
        "id": "R2xvYmFsTGlzdDoxNw==",
        "name": "My new list 15",
        "manager": null,
        "connectionName": null,
        "connectionString": "Data Source=localhost;Initial Catalog=MY_DB;User ID=MY_USER;Password=MyPass$;",
        "providerName": "SYSTEM_DATA_SQLCLIENT",
        "selectCommand": "Select * from USERS"
      }
    }
  }
}
```

### Update a global list

The `updateGlobalList` mutation updates a global list's properties from the global list identifier.

#### Request payload:

```
mutation {
  updateGlobalList(input: {
    id:"R2xvYmFsTGlzdDoxMw==",
    name: "updated name"
  }) {
    globalList {
      id
      name
      manager {
        id
      }
      connectionName
      connectionString
      providerName
      selectCommand
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "updateGlobalList": {
      "globalList": {
        "id": "R2xvYmFsTGlzdDoxMw==",
        "name": "updated name",
        "manager": null,
        "connectionName": null,
        "connectionString": null,
        "providerName": null,
        "selectCommand": null
      }
    }
  }
}
```

### Update a global list from an XML definition

A global list can be updated from its XML definition. The  `.xml` file is uploaded by using the GraphQL [multipart file upload](#multipart-file-upload) feature as shown below.

#### curl request:

```
curl -X POST http://localhost/wfgen/graphql -H "content-type: multipart/form-data" -F "operations={ \"query\": \"mutation ($fileUpload1: Upload) { updateGlobalListFromXmlDefinition(input: { id: \\\"R2xvYmFsTGlzdDow\\\"  xmlDefinition: { upload: $fileUpload1} }) { globalList { name } } }\", \"variables\": { \"fileUpload1\": null } }" -F "map={ \"1\": [\"variables.fileUpload1\"] }" -F "1=@C:\importedlist.xml"
```

#### Response:

```
{
  "data": {
    "updateGlobalListFromXmlDefinition": {
      "globalList": {
        "name": "importedlist"
      }
    }
  }
} 
```

### Delete a global list

The `deleteGlobalList` mutation deletes a global list using the global list identifier.

#### Request payload:

```
mutation {
  deleteGlobalList(input: {
    id: "R2xvYmFsTGlzdDow"
  }) {
    clientMutationId
  }
}
```

#### Response payload:

```
{
  "data": {
    "deleteGlobalList": {
      "clientMutationId": null
    }
  }
}
```

## User and group mutations

### Create a user

Only administrators can create a user.

#### Request payload:

```
mutation {
  createUser(input: {
    userName: "emily_taylor",
    lastName: "Taylor",
    firstName: "Emily",
    commonName: "Emily",
    directoryId: "RGlyZWN0b3J5OjM=",
    password: "TestPassword$",
    fax: "1234567",
    jobTitle: "Engineer",
    systemIdentifier: "70f63326-3947-4052-8bf5-ce4912e7e7b5",
    managerId: "VXNlcjoz",
    mobile: "422 543 8765",
    phone: "666 543 9839",
    postalCode: "R6H 8K0",
    office: "444 Main St",
    postalAddress: "333 Bank St",
    isAdministrator: true,
    isActive: true
  }) {
    user {
      id
      userName
      firstName
      lastName
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "createUser": {
      "user": {
        "id": "VXNlcjox",
        "userName": "emily_taylor",
        "firstName": "Emily",
        "lastName": "Taylor"
      }
    }
  }
}
```

### Update a user

Only administrators can update a user.

#### Request payload:

```
mutation {
  updateUser(input: {
    id: "VXNlcjox"
    userName: "jane_doe",
    lastName: "Doe",
    firstName: "Jane",
    commonName: "Jane",
    directoryId: "RGlyZWN0b3J5OjM=",
    fax: "1234567",
    systemIdentifier: "70f63326-3947-4052-8bf5-ce4912e7e7b5",
    isAdministrator: false,
    isActive: false
  }) {
    user {
      id
      userName
      firstName
      lastName
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "updateUser": {
      "user": {
        "id": "VXNlcjox",
        "userName": "jane_doe",
        "firstName": "Jane",
        "lastName": "Doe"
      }
    }
  }
}
```

### Delete a user

Only administrators can delete a user.

#### Request payload:

```
mutation {
  deleteUser(input: {
    id: "VXNlcjox"
  }) {
    clientMutationId
  }
}
```

#### Response payload:

```
{
  "data": {
    "deleteUser": {
      "clientMutationId": null
    }
  }
}
```

### Add groups to a user

Only administrators can add groups to a user.

#### Request payload:

```
mutation {
  addGroupsToUser(input: {
    id: "VXNlcjox",
    groupIds: ["R3JvdXA6Mw==", "R3JvdXA6Nw=="]
  }) {
    user {
      id
      userName
      firstName
      lastName
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "addGroupsToUser": {
      "user": {
        "id": "VXNlcjox",
        "userName": "jane_doe",
        "firstName": "Jane",
        "lastName": "Doe"
      }
    }
  }
}
```

### Remove groups from a user

Only administrators can remove groups from a user.

#### Request payload:

```
mutation {
  removeGroupsFromUser(input: {
    id: "VXNlcjox",
    groupIds: ["R3JvdXA6Nw=="]
  }) {
    user {
      id
      userName
      firstName
      lastName
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "removeGroupsFromUser": {
      "user": {
        "id": "VXNlcjox",
        "userName": "jane_doe",
        "firstName": "Jane",
        "lastName": "Doe"
      }
    }
  }
}
```

### Create a group

Only administrators can create a group.

#### Request payload:

```
mutation {
  createGroup(input: {
    name: "Group G",
    description: "Group G",
    directoryId: "RGlyZWN0b3J5OjM=",
    isSynchronized: true,
    email: "group_g@company.com",
    systemIdentifier: "13499ebd-f6d6-45c6-8c50-2a1fdc6336cc"
  }) {
    group {
      id
      name
      description
      email
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "createGroup": {
      "group": {
        "id": "R3JvdXA6MQ==",
        "name": "Group G",
        "description": "Group G",
        "email": "group_g@company.com"
      }
    }
  }
}
```

### Update a group

Only administrators can update a group.

#### Request payload:

```
mutation {
  updateGroup(input: {
    id: "R3JvdXA6MQ==",
    name: "Group E",
    description: "Group E",
    email: "group_e@company.com",
    query: "CITY='NEW YORK'"
  }) {
    group {
      id
      name
      description
      email
      query
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "updateGroup": {
      "group": {
        "id": "R3JvdXA6MQ==",
        "name": "Group E",
        "description": "Group E",
        "email": "group_e@company.com",
        "query": "CITY='NEW YORK'"
      }
    }
  }
}
```

### Delete a group

Only administrators can delete a group.

#### Request payload:

```
mutation {
  deleteGroup(input: {
    id: "R3JvdXA6MQ=="
  }) {
    clientMutationId
  }
}
```

#### Response payload:

```
{
  "data": {
    "deleteGroup": {
      "clientMutationId": null
    }
  }
}
```

### Add users to a group

Only administrators can add users to a group.

#### Request payload:

```
mutation {
  addUsersToGroup(input: {
    id: "R3JvdXA6MQ==",
    userIds: ["VXNlcjox", "VXNlcjoy"]
  }) {
    group {
      id
      name
      description
      email
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "addUsersToGroup": {
      "group": {
        "id": "R3JvdXA6MQ==",
        "name": "Group E",
        "description": "Group E",
        "email": "group_e@company.com"
      }
    }
  }
}
```

### Remove users from a group

Only administrators can remove users from a group.

#### Request payload:

```
mutation {
  removeUsersFromGroup(input: {
    id: "R3JvdXA6MQ==",
    userIds: ["VXNlcjoy"]
  }) {
    group {
      id
      name
      description
      email
    }
  }
}
```

#### Response payload:

```
{
  "data": {
    "addUsersToGroup": {
      "group": {
        "id": "R3JvdXA6MQ==",
        "name": "Group E",
        "description": "Group E",
        "email": "group_e@company.com"
      }
    }
  }
}
```

## Pagination

The GraphQL API supports page number based pagination. You can set a page `number` and a `size`; otherwise, the default values are:

* `1` for the page number
* `30` for the page size

The maximum value for the page size is `100`. You can change this setting in the `GraphqlMaxPageSize` key in the `\wfgen\web.config` file.

The result contains:

* `totalCount`: The total number of items
* `hasPreviousPage`
* `hasNextPage`
* `items`: The list of items in the requested page

```
{
  viewer {
    requests(
      page: {number: 2, size: 20}, 
      filter: {as: REQUESTER, status: OPEN}, 
      orderBy: {field: NUMBER, direction: DESC}
    ) {
      totalCount
      hasNextPage
      hasPreviousPage
      items {
        id
        number
        requester {
          userName
        }
      }
    }
  }
}
```

To retrieve the total count without the list of items, you just need to set the page number to `0`:

```
{
  viewer {
    requests(page: {number: 0}, filter: {as: REQUESTER, status: OPEN}) {
      totalCount
      hasNextPage
      hasPreviousPage
    }
  }     
}
```

## File download

As of WorkflowGen version 7.12.0, file data can be downloaded using a blob URL that is returned in the process, request or action datasets, respectively.

### Query

```
{
  requests {
    items {
      dataset {
        items {
          fileValue {
            name
            description
            blobUrl
          }
        }
      }
    }
  }
}
```

### Response

```
{
  "data": {
    "requests": {
      "items": [
        {
          "dataset": {
            "items": [
               "fileValue": {
                  "name": "test.txt",
                  "description": "test.txt",
                  "blobUrl": "http://localhost/wfgen/graphql/workflow/data/files/V29ya2Zsb3dEYXRhRmlsZToxNzEtLS0x"
                }
             ]
          }
        }
      ]
    }
  }
```

## File upload

As of version 7.2.0, GraphQL supports the `fileValue.updatedAt`, `fileValue.content`, and `fileValue.url` fields when sending FILE parameters (as shown in the previous example).

The `fileValue.updatedAt` field should use the ISO 8601 date format.

### File content

The `fileValue.content` field should contain the file content encoded in base64. In this case, the `fileValue.url` field is not required. You must set the maximum input file content size (see the [Configuration](#configuration) section above for instructions on how to set these).

```
...
parameters: {
  name: "FILE", 
  fileValue: {
    name: "test.txt", 
    description: "Test", 
    contentType: "plain/text", 
    size: 74, 
    updatedAt: "2017-03-15T15:02:00Z",
    content: "TG9yZW0gSXBzdW0gaXMgc2ltcGx5IGR1bW15IHRleHQgb2YgdGhlIHByaW50aW5nIGFuZCB0eXBlc2V0dGluZyBpbmR1c3RyeS4="
  }
}
...

```

### File URL&#x20;

The `fileValue.url` field contains the file URL. When working with FILE parameters, you must set the input file allowed folders and the maximum input file size (see the [Configuration](#configuration) section above for instructions on how to set these).&#x20;

You can also prevent file uploads using HTTP and/or HTTPS URLs setting the **Input file allowed HTTP URLs** in the **GraphQL** section on the **Integration** tab in the Administration Module **Configuration Panel** (see[ Input file allowed HTTP URLs](https://docs.advantys.com/docs/admin/configuration-panel#input-file-allowed-http-urls) in the WorkflowGen Administration Guide).

The following path patterns are supported:

**Local file** should use the File URI scheme:

```
...
parameters: {
  name: "FILE", 
  fileValue: {
    name: "test.txt", 
    description: "Test", 
    contentType: "plain/text", 
    url: "file:///c:/temp/test.txt", 
    size: 4714, 
    updatedAt: "2017-03-15T15:02:00Z"
  }
}
...
```

**Public file URL:**

```
...
parameters: {
  name: "FILE", 
  fileValue: {
    name: "update.zip", 
    description: "Update", 
    contentType: "application/zip", 
    url: "http://download.workflowgen.com/product/latest/update.zip", 
    size: 4120858, 
		updatedAt: "2017-03-15T15:02:00Z"
  }
}
...
```

**File URL:**

```
...
parameters: {
  name: "FILE", 
  fileValue: {
    name: "test.txt", 
    description: "Test", 
    contentType: "plain/text", 
    url: "http://localhost:8081/test.txt", 
    size: 4714, 
    updatedAt: "2017-03-15T15:02:00Z"
  }
}
...
```

### Multipart file upload

GraphQL supports multipart file uploads for the following mutations:

* `createRequest`
* `completeAction`
* `completeFormAction`
* `updateRequestDataset`
* `createProcessFromXpdl`
* `updateProcessFromXpdl`
* `createGlobalListFromXmlDefinition`
* `updateGlobalListFromXmlDefinition`

#### 📌 Example for a single upload

**curl**

```
curl -X POST http://localhost/wfgen/graphql -H "content-type: multipart/form-data" -F "operations={ \"query\": \"mutation ($fileUpload1: Upload) { createRequest(input: { processName: \\\"SIMPLE_REQUEST\\\" processVersion: 1 parameters: [ { name:\\\"FILE1\\\" fileValue: { upload: $fileUpload1 } } ] }) { request { number } } }\", \"variables\": { \"fileUpload1\": null } }" -F "map={ \"1\": [\"variables.fileUpload1\"] }" -F "1=@C:\test1.txt"
```

* `content-type` should be `multipart/form-data` <br>

* The `operations` field is required, and contains the GraphQL query:<br>
  * The `Upload` variables should be declared for the mutation:

    `mutation ($fileUpload1: Upload)` <br>
  * `input.parameters[X].fileValue` should contain an `upload` property whose value is a variable previously declared:

    `parameters:[{name:\\\"FILE1\\\" fileValue:{ upload: $fileUpload1}}]` <br>
  * The `variables` values should be set to `null`:

    `\"variables\": { \"fileUpload1\": null }`

* The `map` field is required and contains the file mappings:<br>
  * It should follow the `operations` part.<br>
  * The key should be an alphanumeric string that matches the file key (`"1"` in the above example).<br>
  * The value is the variable that will be assigned to the upload:

    `[\"variables.fileUpload1\"]` <br>
  * For each map entry declared, a file with the same key must be attached.<br>

* Each file to be uploaded should contain the alphanumeric key and the file path:

  `"1=@C:\test1.txt"` <br>

  * Each file attached must match a map entry.<br>
  * The files must follow the `operation` and `map` parts.

**C#**

<pre class="language-clike"><code class="lang-clike"><strong>var query = @"mutation ($fileUpload: Upload) {
</strong>  createRequest(input: {processName: "SIMPLE_REQUEST", processVersion: 1, parameters: [{name: "REQUEST_FILE", fileValue: {upload: $fileUpload}}]}) {
    request {
      number
    }
  }
}
";

var filePath = "DRIVE:\myfile.txt";

var httpClientHandler = new HttpClientHandler
{
    Credentials = new NetworkCredential(username, password)
};
using (var client = new HttpClient(httpClientHandler)
{
    BaseAddress = new Uri(graphqlUrl)
})
{
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(
        new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("multipart/form-data")
    );
    var jsonQuery = JsonConvert.SerializeObject(new
    {
        query,
        variables
    });
    var map = JsonConvert.SerializeObject(new
    {
        file = new[] { "variables.fileUpload" }
    });
    var file = File.ReadAllBytes(filePath);
    var multipartContent = new MultipartFormDataContent
    {
        {new StringContent(jsonQuery), "operations"},
        {new StringContent(map), "map"},
        {new ByteArrayContent(file), "file", Path.GetFileName(filePath)}
    };
    var response = client.PostAsync(string.Empty, multipartContent).Result;
    dynamic result = JsonConvert.DeserializeObject(response.Content.ReadAsStringAsync().Result);
}
</code></pre>

**Node.js**

```
const someFile = new File(); // Or any file from an <input>.
​
(async () => {
	const operations = `{ "query": "mutation ($fileUpload1: Upload) { createRequest(input: { processName: \\\"SIMPLE_REQUEST\\\" processVersion: 1 parameters: [ { name: \\\"FILE1\\\" fileValue: { upload: $fileUpload1 } } ] }) { request { number } } }", "variables": { "fileUpload1": null } }`;
	const formData = new FormData();
	
	formData.append('operations', operations);
	formData.append('map', '{ "0": ["variables.fileUpload1"] }');
	formData.append('0', someFile);
	
	const res = await fetch('http://server:port/wfgen/graphql', {
		method: 'POST',
		body: formData
	});
	
	console.log(await res.text());
})();
```

#### 📌 Example for multiple uploads

**curl**

```
curl -X POST http://localhost/wfgen/graphql -H "content-type: multipart/form-data" -F "operations={ \"query\": \"mutation ($fileUpload1: Upload, $fileUpload2: Upload) { createRequest(input: { processName: \\\"SIMPLE_REQUEST_MULTIPLE_FILES\\\" processVersion: 1 parameters: [ { name: \\\"FILE1\\\" fileValue: { upload: $fileUpload1 } }, { name: \\\"FILE2\\\" fileValue: { upload: $fileUpload2}}] }) { request { number } } }\", \"variables\": { \"fileUpload1\": null, \"fileUpload2\": null } }" -F "map={ \"1\": [\"variables.fileUpload1\"], \"2\": [\"variables.fileUpload2\"] }" -F "1=@C:\test1.txt" -F "2=@C:\test2.txt"
```

### Limitations

* The maximum number of uploads in the same request is 30.<br>
* The maximum file upload size is set in the `GraphqlMaxInputFileSize` configuration parameter.

{% hint style="info" %}
The `multipart/form-data` content type is only supported for HTTP requests with file uploads. If the `fileValue.name`, `fileValue.description`, and `fileValue.contentType` properties are not defined, they will be set from the uploaded file information; otherwise, they will take the values defined the payload.
{% endhint %}

## `viewerAsMember` field usage

The `viewerAsMember` field is a Boolean parameter that determines if the viewer has standard user access scope, even if they have an administrator or a process folder manager profile. It can be used by the `user(userName:"XXX").requests`, `user(userName:"XXX").comments`, and `user(userName:"XXX").actions` queries when an administrator or a process folder manager tries to access another user's requests, comments, or actions with a standard user scope.

In the following payload example, an administrator would be able to access Jane Doe's requests, comments, and actions with a standard user access scope:

```
{
  user(userName: "jane_doe") {
    requests(viewerAsMember: true) {
      totalCount
      hasPreviousPage
      hasNextPage
      items {
        id
        number
      }
    }
    actions(viewerAsMember: true) {
      totalCount
      hasPreviousPage
      hasNextPage
      items {
        id
        number
        request {
          number
        }
      }
    }
    comments(viewerAsMember: true) {
      totalCount
      hasPreviousPage
      hasNextPage
      items {
        id
        subject {
          id
        }
        message
        author {
          id
        }
      }
    }
  }
}
```

## Logs

All HTTP queries are logged by IIS as other ASP.NET web apps. Node.js application logs are available in the `\wfgen\graphql\iisnode\` folder. You can adjust the iisnode log file management in the `\wfgen\graphql\web.config` file.

## Debug mode

A debug mode can be enabled by setting the `GraphqlDebugEnabled` key to `Y` in the `\wfgen\web.config` file.

In debug mode, some extensions are added to the GraphQL response, and additional error messages are logged in the `\wfgen\graphql\iisnode\` folder.

## GraphQL desktop client

If you need to work in GraphQL without an internet connection, you can use the [Altair GraphQL Client](https://altair.sirmuel.design/) for offline access.

* If you're using **Basic** authentication for GraphQL, download the application from <https://altair.sirmuel.design/#download>, then set the **Authorization** header.<br>
* If you're using **Windows** authentication for GraphQL, you can install Altair as a browser extension. No header is required.<br>
  * For Chrome: <https://chrome.google.com/webstore/detail/altair-graphql-client/flnheeellpciglgpaodhkhmapeljopja><br>
  * For Firefox: [https://addons.mozilla.org/en-US/firefox/addon/altair-graphql-client](https://addons.mozilla.org/en-US/firefox/addon/altair-graphql-client/)\ <br>
