Custom Connectors : Zero to Hero - Part I

by Ranul Pallemulle

Introduction

Custom connectors are Flowdoh's way of connecting to an API of your choosing. Installing a custom connector in Flowdoh requires a swagger file, metadata about the service (name, icon and theme color), and API access credentials (if applicable). In this article you will learn how to write a swagger file. In a future article we shall explore installing the connector in Flowdoh and using it within a workflow.

Motivation

Flowdoh has been improving steadily in its ability to connect with and use external APIs. Examples of such integrations include the Microsoft Outlook API and the Google Cloud Translations API. This is made possible by the use of Swagger files. In order to be able to connect any external API of choice it is necessary to understand how Flowdoh uses Swagger files and how to write them. In this document we will explore Swagger files and how to structure them for use with Flowdoh.

Background

A Swagger file is a text file that defines an API. It can be written in JSON format or YAML format. It contains such information as the API host (the URL to interact with), the authentication method(s) to use, the different operations that are available, and much more. What makes this file a "Swagger" file is the structure of the data within it. The structure is defined by the OpenAPI Specification (formerly the Swagger specification). In essence, the goal of the specification is to provide a document format that is sufficiently generic such that it can define the majority of APIs that are available. The format should be expressive enough to be human-readable, and it should be precise enough for a computer to parse and gather all the information needed to interact with the API. As a result of improvements on both fronts, several versions of the OpenAPI specification have emerged. There are two major versions at the time of writing - 2.0 and 3.0. Version 3.0 has several patch versions, such as 3.0.1 and 3.0.2.

OpenAPI 3.0 overcomes many of the limitations of version 2.0. Any API that can be defined using OpenAPI 2.0 can also be defined using OpenAPI 3.0, perhaps even with greater precision. Moreover, certain features in Flowdoh, such as Triggers from external applications, can only be defined using OpenAPI 3.0. As such, we will limit the discussion in this article to OpenAPI 3.0.

All current versions of Flowdoh require Swagger files to be defined in JSON format. Therefore, all examples in this document will be in JSON format. However, it is not a difficult task to convert a JSON-formatted Swagger file to YAML or vice-versa.

Writing a Swagger File

A minimal Swagger file

Listing 1 (👇) shows all the required fields as defined by the OpenAPI specification. Note that a minimally valid Swagger file according the specification is not a valid Flowdoh Swagger file. There are certain fields in the Swagger file that, despite being optional in the OpenAPI specification, are required by Flowdoh to be present. For example, Flowdoh requires the servers section to be defined so that the base URL of the API is known.

Listing 1 : A minimal OpenAPI 3.0 definition ⤵️

{
    "openapi": "3.0.2",
    "info": {
        "title": "A minimal Swagger file",
        "version": "1.0"
    },
    "paths": {

    }
}

Note that the paths object is required but may be left empty. According to the specification, this is to allow for access control - a person may be able to access the Swagger file but not have access to the available paths. Flowdoh requires that Swagger files have a populated paths section as it expects to discover operations that can be invoked on those paths.

The openapi section

This field defines the version of the OpenAPI specification that the file conforms to. It is only valid in OpenAPI 3.0 files. If the file conforms to the OpenAPI 2.0 specification, it should instead have a swagger section as shown in listing 2.

Listing 2 : Alternative to the openapi section in OpenAPI 2.0 ⤵️

{
    "swagger": "2.0"
}

Flowdoh only checks for the first (major) portion of the version number to identify whether the file should be parsed as OpenAPI 2.0 or 3.0. Therefore, any minor or patch version number for OpenAPI 3.0 is regarded as valid. For example, the version number could be indicated as "3.0.9" even though such a version of the specification does not exist. Flowdoh will parse any OpenAPI 3.0 file as if it were of version 3.0.2 as that is the latest officially supported version in Flowdoh. Given that the OpenAPI 3 specification is designed to be backwards compatible, Flowdoh is able to parse all versions of OpenAPI 3.0 documents, but will ignore any features that are added in versions 3.0.3 and later.

The info section

Listing 3 : A fully populated info section ⤵️

{
    "info": {
        "title": "The Food API",
        "description": "This file describes a fictional API that provides details about food items",
        "termsOfService": "https://myfoodapi.com/termsofservice",
        "contact": {
            "name": "Food API Support",
            "url": "https://myfoodapi.com/support",
            "email": "[email protected]"
        },
        "license": {
            "name": "GPLv3",
            "url": "https://myfoodapi.com/LICENSE.html"
        },
        "version": "v15.0"
    }
}

This section is used to add some metadata about the API. The title field gives a name to the API. The version field provides the version number of the API that is being described. Other information such as contact details for support, terms of service and license details may optionally be provided. Flowdoh looks for the title and version fields in this section, but only for the purposes of saving the app. No actual functionality in Flowdoh makes use of the info section. Nevertheless, it should still be included in any Swagger file given it is a required field and provides some benefit to the human reader in characterizing the API the file describes.

The servers section

Listing 4 : A URL defined in the servers section ⤵️

{
    "servers": [
        {
            "url": "https://myapi.com"
        }
    ]
}

This section defines the base URLs for the API. It is possible to include multiple URLs here. Currently, Flowdoh only uses the first one listed. The OpenAPI specification allows for the use of server variables which can be used for substituting values in the URL. This feature is not currently supported in Flowdoh. The specification also allows a server URL to be a relative path which would indicate that the URL is relative to the location of the Swagger file itself. This too is not supported by Flowdoh - it is expected that the server URL is an absolute URL.

The paths section

This is the section within which all operations that can be called against the API are defined. Each path in this section can be appended to the host URL to produce a URL that corresponds to the resource being described.

Listing 5 : The paths object ⤵️

{
    "paths": {
        "/api/v5/users": {
            "get": {
                "summary": "Get list of users",
                "operationId": "getListOfUsers",
                "responses": {
                    "200": {
                        "description": "Successful retrieval of users",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/UsersList"
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

Listing 5 (☝️) shows the paths object populated with a single path corresponding to the server path /api/v5/users. If the servers section of the Swagger file were to contain a URL such as in Listing 4 (☝️) , it could be deduced that the absolute URL for this path is https://myapi.com/api/v5/users. That is, the path is always appended to the server URL to obtain the absolute URL to the resource. There can be many paths defined in the paths section. For example, there may be a different path with the relative URL /api/v5/users/me with a similar get operation defined within it.

Operations

Just as there can be many paths defined within the paths section, there can be many operations defined within a single path. Operations correspond to the HTTP methods GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS and TRACE. The combination of a path and method correspond to a single operation that can be carried out against the API. Flowdoh currently supports the HTTP methods GET, POST, PUT, DELETE and PATCH.

An operation may require some parameters to be provided before it can be invoked. In a HTTP request, parameters may be provided as part of the URL, in the request body, in cookies, and in the request headers. URL parameters may be in the form of path parameters or query parameters. The OpenAPI specification allows for such parameters to be defined as part of an operation.

Listing 6: A POST request with parameters ⤵️

{
    "paths": {
        "/api/v5/{organizationId}/users": {
            "post": {
                "summary": "Create a new user in an organization",
                "operationId": "createUser",
                "parameters": [
                    {
                        "name": "organizationId",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "integer",
                            "format": "int32"
                        }
                    },
                    {
                        "name": "sendEmail",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "boolean",
                            "default": false
                        }
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/User"
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Successfully created the user"
                    }
                }
            }
        }
    }
}

Listing 6 (☝️) shows an operation that takes path, query, and body parameters. The path URL makes use of path templating in order to define path parameters. When the operation is invoked, {organizationId} is replaced using the value provided by the user, and a query string is appended to the URL using the value provided for the sendEmail parameter.

Parameters may also be provided at the path level rather than the operation level. These parameters then apply to all operations defined within the path.

The responses object is a required field. It defines the possible responses of the API to the invocation of the operation. A given response may or may not contain data, but will have an associated response code, for example 201 in Listing 6 (☝️) . 2xx response codes indicate a successful operation and Flowdoh uses such a code as an indication that the operation completed without any errors. If a different code such as one in the range 4xx or 5xx is encountered, Flowdoh assumes the operation failed and will not proceed with the workflow. The keyword default may be used in the Swagger file to describe responses that are not specifically listed under a response code. Flowdoh treats such a definition as that of a failed operation (i.e. similar to a 4xx or 5xx response). In other words, if data from the API's response is to be used within the workflow, the response must be defined under a 2xx code in the Swagger file.

The OpenAPI specification allows for URL, cookie, and header parameters to contain a content property instead of a schema property for more complex scenarios. This is not supported by Flowdoh; Flowdoh always expects a parameter to contain a schema object. Flowdoh also does not currently support cookie parameters. Flowdoh does not permit URL and header parameters to be of a non-primitive type. That is, these parameters must be of type string, integer, number, or boolean, and cannot be of type object or array. However, Flowdoh permits the requestBody schema to contain any valid OpenAPI 3.0 data type.

Flowdoh is able to handle JSON arrays in the response or request body as long as they are defined as the JSON root, or are defined within a JSON object. Flowdoh is currently unable to handle a situation where an array is defined within another array.

Flowdoh also allows aggregate schemas to be defined using the allOf, anyOf, and oneOf keywords. The keyword not is not currently supported.

The components section

This section contains reusable objects that can be referenced from other parts of the Swagger file by using the $ref keyword. For example, Listing 7(👇) shows the UsersList and User schemas referenced by the operations in Listings 5(☝️) and Listings 6(☝️). Items inside the components section can also reference other items in the components section.

Listing 7 : Components section with schemas ⤵️

{
    "components": {
        "schemas": {
            "UsersList": {
                "type": "array",
                "items": {
                    "$ref": "#/components/schemas/User"
                }
            },
            "User": {
                "type": "object",
                "properties": {
                    "firstName": {
                        "type": "string"
                    },
                    "lastName": {
                        "type": "string"
                    },
                    "age": {
                        "type": "integer",
                        "format": "int32"
                    },
                    "address": {
                        "type": "object",
                        "properties": {
                            "city": {
                                "type": "string"
                            },
                            "country": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        }
    }
}

The components section is only necessary if other sections have references to it. The UsersList and User schemas could alternatively have been defined in-line, in which case the components section could be omitted, provided there are no other references to it. The components section can contain other types of reusable objects as well such as parameters and responses .

Security

The OpenAPI specification allows for the available authentication methods to be defined in the Swagger file. It also allows the application of these methods to all operations, or specific operations only. Flowdoh currently does not use any authentication information defined in the Swagger file. Instead, the user must manually input all authentication details during app registration. Furthermore, Flowdoh applies the same authentication method and access level requirements to all available operations in the Swagger file.

Listing 8 : OAuth2 authorization code flow defined in the Swagger file ⤵️

{
    "paths": {
        "/api/send": {
            "post": {
                "security": {
                    "myApiAuth": [
                        "api:read",
                        "api:write"
                    ]
                }
            }
        }
    },
    "security": {
        "myApiAuth": [
            "api:read"
        ]
    }
    "components": {
        "securitySchemes": {
            "myApiAuth": {
                "type": "oauth2",
                "description": "OAuth2 authorization for my API",
                "flows": {
                    "authorizationCode": {
                        "authorizationUrl": "https://auth.myapi.com/authorize",
                        "tokenUrl": "https://auth.myapi.com/token",
                        "refreshUrl": "https://auth.myapi.com/token",
                        "scopes": {
                            "api:read": "Grants access to read data",
                            "api:write": "Grants access to write data"
                        }
                    }
                }
            }
        }
    }
}

Listing 8(☝️) shows how OAuth2 authorization with different scopes may be defined in the Swagger file. In this example are two scopes available - api:read and api:write. The myApiAuth authentication method is applied at the root security object with scope api:read. This means that all operations defined in the Swagger file require the user to have permission to read data, but does not require the user to have permission to write data. This requirement is overridden by the /api/send POST operation which requires elevated privileges - the user must have permission to both read from and write data to the API. Given that Flowdoh does not use security information from the Swagger file and that the same permissions are requested for all operations, all possible scopes must be requested during app registration. That is, both api:read and api:write scopes must be specified, even if most endpoints only need one of the scopes or no authentication at all. For other authentication methods, the highest access level required must be requested.

Flowdoh Actions

In this section we will focus on topics that are more geared towards correctly defining Flowdoh Actions and how to use specific properties in the Swagger file to achieve some desired effect.

Defining Actions

By default, Flowdoh recognizes an operation within a given path in the Swagger file as an Action. While the OpenAPI specification allows for a large number of media types to be specified for operation parameters and responses, Flowdoh currently only supports the media types application/json, application/octet-stream, application/x-www-form-urlencoded and multipart/form-data for use with Actions and Triggers. Actions also support common binary media types in responses (see section on Binary data for more details).

Action parameters and responses are defined in the Swagger file as described in the Operations section of this document. Parameters for which values aren't provided by the user will not be sent as part of the operation if they aren't required parameters and no default value is defined in the Swagger file. If they are required parameters, a suitable empty value will be sent. For example, if the parameter type is string the value "" will be sent.

In addition, several custom properties, also known as Swagger extensions, can be defined. Extensions are application-specific properties that are not governed by the OpenAPI specification. Flowdoh-specific extensions have names that are prefixed with x-zen- and are summarised in Table 1(👇)

Extension
Description

x-zen-action

Alternative to operationId as a name for the action. May contain spaces.

x-zen-trigger

Similar to x-zen-action but also identifies the operation as a Trigger and not an Action.

x-zen-action-trigger

Similar to x-zen-action and x-zen-trigger, but identifies the operation as an Action Trigger. (These are triggers which can be registered under an Action Node in a Workflow)

x-zen-display-name

Alternative display name for a parameter or context item.

x-zen-hidden-input

Prevents a parameter from being displayed to the user in the Action configuration.

x-zen-callback-url

Identifies the callback URL parameter in the Trigger webhook request.

x-zen-array-index

Selects a specific index of an array in an Action/Trigger response, rather than using the entire array.

x-zen-hook-renew-period

Defines a time duration (in minutes) after which a Trigger webhook should be automatically re-established.

The x-zen-action property

The operationId property is used to uniquely identify an operation within the Swagger file. If the x-zen-action property is not present, the value of the operationId field will be displayed in the Action selection list when inserting a new Action into a workflow. operationId is typically supplied as a name without spaces - e.g. GetUserById. Therefore, it might be a good idea to provide a separate name for the action for display purposes. The displayed name may be overridden by specifying the x-zen-action property. Note that even if the x-zen-action property is specified, it is recommended that the operationId property is still supplied as it may be used for other purposes such as linking operations together (see section on Triggers).

Listing 9 : x-zen-action usage ⤵️

{
    "paths": {
        "/api/v5/users": {
            "get": {
                "summary": "Get list of users",
                "operationId": "getListOfUsers",
                "x-zen-action": "Get List of Users"
            }
        }
    }
}

The x-zen-display-name property

Similar to the x-zen-action property, this property provides an alternative display name to a field defined inside a schema property. It is applicable to parameters as well as responses. For parameters, the custom values are displayed in the Action configuration panel. For responses, the custom values are displayed in the context selection panel.

Listing 9 : x-zen-display-name usage ⤵️

"requestBody": {
    "content": {
        "application/json": {
            "schema": {
                "type": "object",
                "properties": {
                    "firstName": {
                        "type": "string",
                        "x-zen-display-name": "First Name"
                    },
                    "address": {
                        "type": "object",
                        "properties": {
                            "city": {
                                "type": "string",
                                "x-zen-display-name": "City of Residence"
                            },
                            "country": {
                                "type": "string",
                                "x-zen-display-name": "Country of Residence"
                            }
                        }
                    }
                }
            }
        }
    }
}

The x-zen-array-index property

Some actions (and triggers) return data that contain JSON arrays. In Flowdoh, data inside arrays can only be accessed in the workflow context by using a loop which will do some processing for each element of the array. In some cases it may be known that only a single element of the array is useful to the workflow. This property may then be used to extract the data out of the array so that it can be used in the workflow context without the use of loops.

Listing 9 : x-zen-array-index usage ⤵️

"responses": {
    "200": {
        "description": "Success",
        "content": {
            "application/json": {
                "schema": {
                    "type": "object",
                    "properties": {
                        "value": {
                            "type": "array",
                            "x-zen-array-index": 0,
                            "items": {
                                "type": "object",
                                "properties": {
                                    "messageId": {
                                        "type": "string"
                                    },
                                    "messageContent": {
                                        "type": "string"
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

In Listing 11(☝️), if the x-zen-array-index property were not specified, the value property must be mapped to a loop start node before the properties messageId and messageContent are available as context data (and even then they will only be available inside the loop). The inclusion of x-zen-array-index indicates to Flowdoh that only the first item of the array (index 0) should be used and not the entire array. This allows messageId and messageContent to be available in the context just like any other context item.

Binary data

A user can use Actions to send binary data such as documents and images to external APIs. Binary data can also be received from an external API as part of the action response or trigger. This can be achieved by using the media types application/json or multipart/form-data in the requestBody or responses section of an operation. In the case of actions that return plain binary data, media types such as image/jpeg, image/png, or application/pdf can be used. If the action may return various file types, the generic media type application/octet-stream may be used. To identify plain binary data in the workflow context, use the x-zen-display-name property (see Listing 14(👇)). In the requestBody, if application/json is used, the file will be sent as a base64-encoded string. Else, if multipart/form-data is used, the file will be sent as raw binary data. To identify an item as binary content within the Swagger file, it must be given the type string. If the media type is application/json, the item must be given the format byte. If the media type is multipart/form-data, or is a different file media type such as application/pdf, the item must be given the format binary.

Listing 12: Defining files in JSON data ⤵️

"requestBody": {
    "content": {
        "application/json": {
            "schema": {
                "type": "object",
                "properties": {
                    "fullName": {
                        "type": "string"
                    },
                    "address": {
                        "type": "string"
                    },
                    "resume": { // binary data
                        "type": "string",
                        "format": "byte"
                    }
                }
            }
        }
    }
}

Listing 13: Defining files in a form post ⤵️

"requestBody": {
    "content": {
        "multipart/form-data": {
            "schema": {
                "type": "object",
                "properties": {
                    "fullName": {
                        "type": "string"
                    },
                    "address": {
                        "type": "string"
                    },
                    "resume": { // binary data
                        "type": "string",
                        "format": "binary"
                    }
                }
            }
        }
    }
}

Listing 14: Response returning a file ⤵️

"responses": {
    "200": {
        "description": "A pdf or image file",
        "content": {
            "application/octet-stream": {
                "schema": {
                    "type": "string",
                    "format": "binary",
                    "x-zen-display-name": "File Content"
                }
            }
        }
    }
}

Flowdoh Triggers

As of Flowdoh 2.2.5.0, Flowdoh supports two types of triggers.

Default Triggers

Default Triggers are registered on the start node of a Workflow. These triggers can be specified on a Swagger file with the x-zen-trigger property. These triggers are used to trigger a new instance of a given Workflow.

Action Node Triggers / Long Wait Triggers

Action Node Triggers is a new feature introduced in Flowdoh 2.2.5.0. These allow Action nodes to register Triggers as well. By registering an Action Trigger, a given Workflow will pause execution at the specific Action Node and can be triggered at a later time to resume execution of the Workflow. This allows Workflows to paused and resumed based on events triggered by an external application. Action Triggers can be specified with x-zen-action-trigger on your Swagger file.

Webhooks

Before we can define a Flowdoh Trigger in a Swagger file, we must first understand the concept of a webhook. A webhook is similar to a contract between two applications, where one application sends a URL to the other and asks it to send a request with data to that URL when some condition is met.

Flowdoh makes use of webhooks to enable triggers from external applications. Flowdoh creates a URL which, when sent a request with data, triggers a given workflow. This URL is called a callback URL. Flowdoh sends a request containing this URL to the external application, along with some conditions as to when to trigger the workflow. This is called a webhook subscribe request. The external application stores the callback URL and the conditions. Then, at some point in the future when the conditions are met, the external application sends a notification to the callback URL, triggering the workflow. If at any point the workflow must be deactivated, Flowdoh sends the API a request asking it to stop sending notifications. This is called a webhook unsubscribe request, and must contain some information about the initial subscription so that the API can identify it and cancel it.

A trigger can be defined in the Swagger file by describing three parts that make up the webhook; the webhook subscribe request, the notifications, and the webhook unsubscribe request. The following sections illustrate how each part is described. For complete examples of describing triggers, see the Outlook API and the Florix Feedback Swagger files in the Appendices.

If the external API does not support webhooks, an alternative way to define a trigger is to use a Hookless trigger.

The webhook subscribe request

Listing 15(👇) shows an example of a webhook subscribe request. The requestBody, callbacks, and responses sections are left empty here - we shall describe them separately.

Listing 15: Webhook subscribe request ⤵️

"paths": {
    "/api/webhooks/subscribe": {
        "post": {
            "summary": "Subscribe to message received notifications",
            "operationId": "subscribeToNotifications",
            "x-zen-trigger": "Message Received",
            "requestBody": {

            },
            "callbacks": {

            },
            "responses": {
                
            }
        }
    }
}

The x-zen-trigger property identifies the operation being described as a webhook subscribe request. It also has a similar effect to x-zen-action in that it gives the trigger a custom display name. Action Node triggers can be specified with x-zen-action-trigger . All of the other configuration steps are the same.

Listing 16: Webhook subscribe request - the request body ⤵️

"requestBody": {
    "content": {
        "application/json": {
            "schema": {
                "type": "object",
                "properties": {
                    "subscriptionUrl": {
                        "x-zen-callback-url": "subscriptionUrl",
                        "x-zen-hidden-input": true,
                        "type": "string"
                    },
                    "senderPhoneNumber": {
                        "type": "string"
                    }
                }
            }
        }
    }
}

For Default Triggers, The webhook subscribe request is sent by Flowdoh as soon as a new workflow is saved, or when a stopped workflow is restarted. For Action Node Triggers, the webhook subscribe request is sent once the Workflow execution reaches the Action node for which the Action Trigger is registered.

Listing 16(☝️) shows an example request body in a webhook subscribe request. The subscriptionUrl property takes the workflow trigger callback URL as its value. This is indicated to Flowdoh by using x-zen-callback-url property. Flowdoh then generates the callback URL and populates the value of the subscriptionUrl property. Since the value is populated by the system and not the user, the parameter need not be visible in the trigger configuration panel. Hence, the x-zen-hidden-input property is used here to hide it.

Listing 17: Webhook subscribe request - the callbacks section ⤵️

"callbacks": {
    "trigger": {
        "{request.body#/subscriptionUrl}": {
            "post": {
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "sender": {
                                        "type": "object",
                                        "properties": {
                                            "name": {
                                                "type": "string"
                                            },
                                            "phoneNumber": {
                                                "type": "string"
                                            }
                                        }
                                    },
                                    "message": {
                                        "type": "string"
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

The callbacks section allows for asynchronous notifications to be defined in a Swagger file. Listing 17(☝️) shows an example of a notification sent by the external API to Flowdoh to trigger the workflow. This request must be a POST request as Flowdoh only accepts POST requests to callback URLs. The specification allows for multiple callbacks to be defined in the callbacks section. To identify the trigger callback, Flowdoh looks for a callback named trigger. If it does not find one, it uses the first listed callback. The requestBody section of the callback describes the data sent as part of the notification. This data that is available in the workflow context. Flowdoh only accepts application/json or multipart/form-data media types in notifications.

Listing 18: Webhook subscribe request - the responses section ⤵️

"responses": {
    "201": {
        "description": "Subscribed",
        "content": {
            "application/json": {
                "schema": {
                    "type": "object",
                    "properties": {
                        "subscriptionId": {
                            "type": "string"
                        }
                    }
                }
            }
        },
        "links": {
            "HookRemove": {
                "description": "Link to webhook unsubscribe request",
                "operationId": "unsubscribeFromNotifications",
                "parameters": {
                    "id": "$response.body#/subscriptionId"
                }
            }
        }
    }
}

The responses section describes the external API's immediate response to a webhook subscribe request. The response typically contains some data, such as an ID, that can be used to identify the subscription so that it may be cancelled or modified later. In the example response in Listing 18(☝️) we see that some JSON data with a property subscriptionId is returned. The responses object also defines a links section. Response links[@linkedoperations] are an OpenAPI 3.0 feature that indicates to the reader that a given operation, identified by its operationId, can use data from another operation as parameters. Listing 18(☝️) shows that the operation unsubscribeFromNotifications uses the subscriptionId returned in the response as its id parameter. The location of the subscriptionId property is described using runtime expression syntax - $response.body#/subscriptionId. For more information on this syntax, see reference-11 . Flowdoh requires that the link connecting the webhook subscribe request and the webhook unsubscribe request be named HookRemove.

The webhook unsubscribe request

Listing 19: Webhook unsubscribe request ⤵️

"paths": {
    "/api/webhooks/{id}": {
        "delete": {
            "summary": "Unsubscribe from notifications",
            "operationId": "unsubscribeFromNotifications",
            "parameters": [
                {
                    "in": "path",
                    "name": "id",
                    "required": true,
                    "schema": {
                        "type": "string"
                    }
                }
            ]
            "responses": {
                "204": {
                    "description": "Unsubscribed"
                }
            }
        }
    }
}

The webhook unsubscribe request cancels the subscription identified by the id parameter. Flowdoh sends this request when a workflow is disabled (stopped) by the user. This parameter is populated by Flowdoh using the response to the webhook subscribe request.

The x-zen-hook-renew-period property

Some external services do not allow indefinite subscriptions. That is, the webhook expires after a certain time has elapsed. After webhook expiration, notifications are no longer sent by the API. This means that Flowdoh must periodically re-establish webhooks with such an API. This can be achieved by using the x-zen-hook-renew-period property.

Listing 20 : x-zen-hook-renew-period usage ⤵️

"paths": {
    "/api/webhooks/subscribe": {
        "post": {
            "summary": "Subscribe to message received notifications",
            "operationId": "subscribeToNotifications",
            "x-zen-trigger": "Message Received",
            "x-zen-hook-renew-period": 2880
        }
    }
}

Listing 20(☝️) indicates that a new webhook will be established every 2880 minutes (2 days). Auto-renewal of the webhook will only take place if the workflow is not disabled (stopped). All of the above configuration applies similarily for Action Node Triggers.

References

[1] OpenAPI Initiative. The OpenAPI Specification.

https://swagger.io/specification - [Accessed 2020-11-01].

[2] OpenAPI Initiative. TDC: Structural Improvements

explaining the 3.0 spec, part 2. https://www.openapis.org/news/blogs/2016/10/tdc-structural-improvements-explaining-30-spec-part-2 - [Accessed 2020-11-01].

[3] OpenAPI Initiative. ACL Constraints - The OpenAPI Specification.

https://swagger.io/specification/#security-filtering - [Accessed 2020-11-01].

[4] OpenAPI Initiative. Server Variables - The OpenAPI Specification.

https://swagger.io/specification/#server-variable-object - [Accessed 2020-11-01].

[5] OpenAPI Initiative. Parameters - The OpenAPI Specification.

https://swagger.io/specification/#parameter-object - [Accessed 2020-11-01].

[6] OpenAPI Initiative. oneOf, anyOf, allOf, not - The OpenAPI Specification.

https://swagger.io/docs/specification/data-models/oneof-anyof-allof-not - [Accessed 2020-11-01].

[7] OpenAPI Initiative. Components - The OpenAPI Specification.

https://swagger.io/docs/specification/components - [Accessed 2020-11-01].

[8] OpenAPI Initiative. Media Types - The OpenAPI Specification.

https://swagger.io/specification/#media-types - [Accessed 2020-11-01].

[9] OpenAPI Initiative. OpenAPI Extensions - The OpenAPI Specification.

https://swagger.io/specification/openapi-extensions - [Accessed 2020-11-01].

[10] OpenAPI Initiative. Linked Operations - The OpenAPI Specification.

https://swagger.io/specification/#linked-operations - [Accessed 2020-11-01].

[11] OpenAPI Initiative. Runtime Expressions - The OpenAPI Specification.

https://swagger.io/specification/#runtime-expressions - [Accessed 2020-11-01].

Appendices

Example Swagger Files

Microsoft Outlook API

{
  "openapi": "3.0.2",
  "info": {
    "description": "Microsoft Outlook",
    "version": "2.0",
    "title": "Outlook API"
  },
  "servers": [
    {
      "url": "https://outlook.office.com"
    }
  ],
  "paths": {
    "/api/v2.0/me/subscriptions": {
      "post": {
        "tags": ["Webhooks"],
        "x-zen-trigger": "Mail Received",
        "x-zen-hook-renew-period": 2880,
        "summary": "Hook for mail retrieval",
        "description": "",
        "operationId": "mailRetrievalHook",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HookRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HookResponse"
                }
              }
            },
            "links": {
              "HookRemove": {
                "description": "Remove webhook",
                "operationId": "deletePushNotificationHook",
                "parameters": {
                  "subscriptionId": "$response.body#/Id"
                }
              }
            }
          }
        },
        "callbacks": {
          "trigger": {
            "{$request.body#/NotificationURL}": {
              "post": {
                "requestBody": {
                  "required": true,
                  "content": {
                    "application/json": {
                      "schema": {
                        "$ref": "#/components/schemas/EmailReceivedPushContent"
                      }
                    }
                  }
                },
                "responses": {
                  "201": {
                    "description": "Workflow instance created",
                    "content": {
                      "application/json": {
                        "schema": {
                          "type": "object",
                          "properties": {
                            "correlationId": {
                              "type": "string",
                              "description": "Http request trace identifier"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "all_mail": [
              "Offline_Access",
              "https://outlook.office.com/mail.read"
            ]
          }
        ]
      }
    },
    "/api/v2.0/me/subscriptions('{subscriptionId}')": {
      "delete": {
        "tags": ["Webhooks"],
        "summary": "Delete push notification hook",
        "description": "",
        "operationId": "deletePushNotificationHook",
        "x-zen-action": "Delete Push Notification Webhook",
        "parameters": [
          {
            "name": "subscriptionId",
            "in": "path",
            "description": "Subscription ID",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          }
        },
        "security": [
          {
            "all_mail": [
              "Offline_Access",
              "https://outlook.office.com/mail.read"
            ]
          }
        ]
      }
    },
    "/api/v2.0/me/messages/{id}": {
      "get": {
        "tags": ["Mails"],
        "summary": "Get mail",
        "description": "",
        "operationId": "getMail",
        "x-zen-action": "Get Mail Message",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Message ID",
            "required": true,
            "schema": {
              "x-zen-display-name": "Message ID",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Message",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "Subject": {
                      "type": "string"
                    },
                    "Body": {
                      "type": "object",
                      "properties": {
                        "ContentType": {
                          "type": "string",
                          "x-zen-display-name": "Content Type"
                        },
                        "Content": {
                          "type": "string"
                        }
                      }
                    },
                    "Sender": {
                      "type": "object",
                      "properties": {
                        "EmailAddress": {
                          "type": "object",
                          "properties": {
                            "Name": {
                              "type": "string",
                              "x-zen-display-name": "Sender Name"
                            },
                            "Address": {
                              "type": "string",
                              "x-zen-display-name": "Sender Address"
                            }
                          }
                        }
                      }
                    },
                    "From": {
                      "type": "object",
                      "properties": {
                        "EmailAddress": {
                          "type": "object",
                          "properties": {
                            "Name": {
                              "type": "string",
                              "x-zen-display-name": "From Name"
                            },
                            "Address": {
                              "type": "string",
                              "x-zen-display-name": "From Address"
                            }
                          }
                        }
                      }
                    },
                    "ToRecipients": {
                      "type": "array",
                      "x-zen-display-name": "Recipients",
                      "items": {
                        "type": "object",
                        "properties": {
                          "EmailAddress": {
                            "type": "object",
                            "properties": {
                              "Name": {
                                "type": "string",
                                "x-zen-display-name": "Recipient Name"
                              },
                              "Address": {
                                "type": "string",
                                "x-zen-display-name": "Recipient Address"
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "all_mail": [
              "Offline_Access",
              "https://outlook.office.com/mail.read"
            ]
          }
        ]
      }
    },
    "/api/v2.0/me/messages/{id}/attachments": {
      "get": {
        "tags": ["Mails"],
        "summary": "Get mail attachments",
        "description": "",
        "operationId": "getMailAttachments",
        "x-zen-action": "Get Mail Attachments",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Message ID",
            "required": true,
            "schema": {
              "x-zen-display-name": "Message ID",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Message",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "value": {
                      "type": "array",
                      "x-zen-display-name": "Attachments",
                      "items": {
                        "type": "object",
                        "properties": {
                          "Name": {
                            "type": "string",
                            "x-zen-display-name": "File Name"
                          },
                          "ContentType": {
                            "type": "string",
                            "x-zen-display-name": "File Content Type"
                          },
                          "ContentBytes": {
                            "type": "string",
                            "x-zen-display-name": "File"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "all_mail": [
              "Offline_Access",
              "https://outlook.office.com/mail.read"
            ]
          }
        ]
      }
    },
    "/api/v2.0/me/sendmail": {
      "post": {
        "tags": ["Mails"],
        "summary": "Send a mail",
        "description": "",
        "operationId": "sendMail",
        "x-zen-action": "Send Mail",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MailMessage"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted"
          },
          "405": {
            "description": "Invalid input"
          }
        },
        "security": [
          {
            "all_mail": [
              "Offline_Access",
              "https://outlook.office.com/mail.send"
            ]
          }
        ]
      }
    }
  },
  "components": {
    "schemas": {
      "MailMessage": {
        "type": "object",
        "required": ["Message"],
        "properties": {
          "Message": {
            "type": "object",
            "title": "The Message Schema",
            "required": ["Subject", "Body", "ToRecipients"],
            "properties": {
              "Subject": {
                "type": "string",
                "title": "The Subject Schema",
                "default": "",
                "pattern": "^(.*)$"
              },
              "Body": {
                "type": "object",
                "title": "The Body Schema",
                "required": ["ContentType", "Content"],
                "properties": {
                  "ContentType": {
                    "type": "string",
                    "x-zen-display-name": "Content Type",
                    "title": "The Contenttype Schema",
                    "default": "html",
                    "pattern": "^(.*)$"
                  },
                  "Content": {
                    "type": "string",
                    "format": "html",
                    "title": "The Content Schema",
                    "default": "",
                    "pattern": "^(.*)$"
                  }
                }
              },
              "ToRecipients": {
                "type": "array",
                "x-zen-display-name": "Recipients",
                "title": "The Torecipients Schema",
                "items": {
                  "type": "object",
                  "title": "The Items Schema",
                  "required": ["EmailAddress"],
                  "properties": {
                    "EmailAddress": {
                      "type": "object",
                      "title": "The Emailaddress Schema",
                      "required": ["Address"],
                      "properties": {
                        "Address": {
                          "type": "string",
                          "title": "The Address Schema",
                          "default": "",
                          "pattern": "^(.*)$"
                        }
                      }
                    }
                  }
                }
              },
              "Attachments": {
                "type": "array",
                "title": "Attachments",
                "items": {
                  "type": "object",
                  "title": "The Items Schema",
                  "properties": {
                    "Name": {
                      "type": "string",
                      "title": "Attachment Name"
                    },
                    "@odata.type": {
                      "type": "string",
                      "x-zen-display-name": "OData Type",
                      "title": "Type",
                      "default": "#Microsoft.OutlookServices.FileAttachment"
                    },
                    "ContentBytes": {
                      "type": "string",
                      "x-zen-display-name": "Content Bytes",
                      "title": "Attachment Content Base64"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "HookRequest": {
        "type": "object",
        "required": [
          "@odata.type",
          "Resource",
          "NotificationURL",
          "ChangeType",
          "ClientState"
        ],
        "properties": {
          "@odata.type": {
            "type": "string",
            "x-zen-display-name": "OData Type",
            "default": "#Microsoft.OutlookServices.PushSubscription"
          },
          "Resource": {
            "type": "string",
            "default": "https://outlook.office.com/api/v2.0/me/mailfolders('inbox')/messages"
          },
          "NotificationURL": {
            "type": "string",
            "x-zen-callback-url": "NotificationURL",
            "x-zen-hidden-input": true
          },
          "ChangeType": {
            "type": "string",
            "x-zen-display-name": "Change Type",
            "default": "Created"
          },
          "ClientState": {
            "type": "string",
            "x-zen-display-name": "Client State",
            "default": "Flowdoh"
          }
        }
      },
      "HookResponse": {
        "type": "object",
        "properties": {
          "Id": {
            "type": "string"
          },
          "SubscriptionExpirationDateTime": {
            "type": "string"
          }
        }
      },
      "EmailReceivedPushContent": {
        "type": "object",
        "properties": {
          "value": {
            "type": "array",
            "x-zen-array-index": 0,
            "items": {
              "type": "object",
              "properties": {
                "SubscriptionId": {
                  "type": "string",
                  "x-zen-display-name": "Subscription ID"
                },
                "SubscriptionExpirationDateTime": {
                  "type": "string",
                  "x-zen-display-name": "Subscription Expiration Date/Time"
                },
                "SequenceNumber": {
                  "type": "integer",
                  "format": "int32",
                  "x-zen-display-name": "Sequence Number"
                },
                "ChangeType": {
                  "type": "string",
                  "x-zen-display-name": "Change Type"
                },
                "Resource": {
                  "type": "string"
                },
                "ResourceData": {
                  "type": "object",
                  "properties": {
                    "Id": {
                      "x-zen-display-name": "Message ID",
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "securitySchemes": {
      "all_mail": {
        "type": "oauth2",
        "description": "API OAuth2 Security",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
            "tokenUrl": "https://login.microsoftonline.com/common/oauth2/v2.0/token",
            "refreshUrl": "https://login.microsoftonline.com/common/oauth2/v2.0/token",
            "scopes": {
              "Offline_Access": "Allows application to access refresh access token on behalf of user",
              "https://outlook.office.com/mail.read": "Read access to user emails",
              "https://outlook.office.com/mail.send": "Write access to user emails"
            }
          }
        }
      }
    }
  },
  "externalDocs": {
    "description": "Find out more about Swagger",
    "url": "http://swagger.io"
  }
}

Florix Feedback

{
  "openapi": "3.0.1",
  "info": {
    "title": "Florix Feedback",
    "version": "v1"
  },
  "servers": [
    {
      "url": "https://florixfeedback.azurewebsites.net"
    }
  ],
  "paths": {
    "/api/v1/Webhooks": {
      "post": {
        "x-zen-trigger": "Feedback Submitted",
        "operationId": "feedbackSubmittedHook",
        "summary": "Webhook creation",
        "tags": [
          "Webhooks"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookCreationDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookResponse"
                }
              }
            },
            "links": {
              "HookRemove": {
                "operationId": "deleteWebhook",
                "parameters": {
                  "subscriptionId": "$response.body#/subscriptionId"
                }
              }
            }
          }
        },
        "callbacks": {
          "trigger": {
            "{$request.body#/callbackUrl}": {
              "post": {
                "requestBody": {
                  "required": true,
                  "content": {
                    "application/json": {
                      "schema": {
                        "$ref": "#/components/schemas/Notification"
                      }
                    }
                  }
                },
                "responses": {
                  "201": {
                    "description": "Workflow instance created",
                    "content": {
                      "application/json": {
                        "schema": {
                          "type": "object",
                          "properties": {
                            "correlationId": {
                              "type": "string",
                              "description": "Http request trace identifier"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteWebhook",
        "summary": "delete webhook",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "name": "subscriptionId",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "WebhookCreationDto": {
        "type": "object",
        "properties": {
          "callbackUrl": {
            "type": "string",
            "x-zen-callback-url": "callbackUrl",
            "x-zen-hidden-input": true 
          },
          "feedbackType": {
            "type": "string"
          }
        },
        "additionalProperties": false
      },
      "WebhookResponse": {
        "type": "object",
        "properties": {
          "subscriptionId": {
            "type": "string"
          }
        }
      },
      "Notification": {
        "type": "object",
        "properties": {
          "Name": {
            "type": "string",
            "nullable": true
          },
          "Email": {
            "type": "string",
            "nullable": true
          },
          "Comments": {
            "type": "string"
          },
          "Anonymous": {
            "type": "boolean"
          }
        }
      }
    }
  }
}

Google Cloud Translations API

{
  "openapi": "3.0.2",
  "info": {
    "title": "Google Cloud Translations",
    "version": "v3"
  },
  "servers": [
    {
      "url": "https://translation.googleapis.com/v3"
    }
  ],
  "paths": {
    "/projects/{project}:translateText": {
      "parameters": [
        {
          "in": "path",
          "name": "project",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "Translations"
        ],
        "x-zen-action": "Translate Text",
        "operationId": "translateText",
        "summary": "Translate text",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TranslateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Ok",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TranslateResponse"
                }
              }
            }
          }
        }
      }
    },
    "/projects/{project}:detectLanguage": {
      "parameters": [
        {
          "in": "path",
          "name": "project",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "Translations"
        ],
        "x-zen-action": "Detect Language",
        "operationId": "detectLanguage",
        "summary": "Detect Language in input",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LangDetectRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Ok",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LangDetectResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "security": [
    {
      "speech_api": [
        "https://www.googleapis.com/auth/cloud-platform",
        "https://www.googleapis.com/auth/cloud-translation"
      ]
    }
  ],
  "components": {
    "schemas": {
      "TranslateRequest": {
        "type": "object",
        "properties": {
          "contents": {
            "x-zen-display-name": "Text to translate",
            "type": "array",
            "items": {
              "x-zen-display-name": "Text",
              "type": "string"
            }
          },
          "targetLanguageCode": {
            "x-zen-display-name": "Target language code (BCP-47)",
            "type": "string"
          }
        }
      },
      "TranslateResponse": {
        "type": "object",
        "properties": {
          "translations": {
            "type": "array",
            "x-zen-array-index": 0,
            "items": {
              "type": "object",
              "properties": {
                "translatedText": {
                  "x-zen-display-name": "Translated Text",
                  "type": "string"
                },
                "detectedLanguageCode": {
                  "x-zen-display-name": "Detected Language Code",
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "LangDetectRequest": {
        "type": "object",
        "properties": {
          "content": {
            "x-zen-display-name": "Text",
            "type": "string"
          }
        }
      },
      "LangDetectResponse": {
        "type": "object",
        "properties": {
          "languages": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "languageCode": {
                  "x-zen-display-name": "Detected Language Code",
                  "type": "string"
                },
                "confidence": {
                  "x-zen-display-name": "Confidence",
                  "type": "integer",
                  "format": "int32"
                }
              }
            }
          }
        }
      }
    },
    "securitySchemes": {
      "speech_api": {
        "type": "oauth2",
        "description": "Speech to Text OAuth2 security",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://accounts.google.com/o/oauth2/v2/auth",
            "tokenUrl": "https://oauth2.googleapis.com/token",
            "refreshUrl": "https://oauth2.googleapis.com/token",
            "scopes": {
              "https://www.googleapis.com/auth/cloud-platform": "Access cloud platform",
              "https://www.googleapis.com/auth/cloud-translation": "Access cloud translation"
            }
          }
        }
      }
    }
  }
}

Google Cloud Speech-to-Text API

{
  "openapi": "3.0.2",
  "info": {
    "title": "Google Speech to Text",
    "version": "v1p1beta1"
  },
  "servers": [
    {
      "url": "https://speech.googleapis.com/v1p1beta1"
    }
  ],
  "paths": {
    "/speech:recognize": {
      "post": {
        "tags": [
          "Transcriptions"
        ],
        "summary": "Transcribe speech to text",
        "x-zen-action": "Speech To Text",
        "operationId": "speechToText",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "config": {
                    "$ref": "#/components/schemas/RecognitionConfig"
                  },
                  "audio": {
                    "$ref": "#/components/schemas/RecognitionAudio"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Ok",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "results": {
                      "type": "array",
                      "x-zen-array-index": 0,
                      "items": {
                        "$ref": "#/components/schemas/SpeechRecognitionResult"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "security": [
    {
      "speech_api": [
        "https://www.googleapis.com/auth/cloud-platform"
      ]
    }
  ],
  "components": {
    "schemas": {
      "RecognitionConfig": {
        "type": "object",
        "required": [
          "languageCode"
        ],
        "properties": {
          "encoding": {
            "type": "string",
            "x-zen-display-name": "Encoding",
            "description": "Encoding of audio data sent in all RecognitionAudio messages. This field is optional for FLAC and WAV audio files and required for all other audio formats.",
            "enum": [
              "ENCODING_UNSPECIFIED", 
              "LINEAR16", 
              "FLAC", 
              "MULAW",
              "AMR",
              "AMR_WB",
              "OGG_OPUS",
              "SPEEX_WITH_HEADER_BYTE",
              "MP3"
            ]
          },
          "sampleRateHertz": {
            "type": "integer",
            "x-zen-display-name": "Sample Rate (Hertz)",
            "description": "Sample rate in Hertz of the audio data sent in all RecognitionAudio messages. Valid values are: 8000-48000. 16000 is optimal. For best results, set the sampling rate of the audio source to 16000 Hz."
          },
          "audioChannelCount": {
            "type": "integer",
            "x-zen-display-name": "Audio Channel Count",
            "description": "The number of channels in the input audio data. ONLY set this for MULTI-CHANNEL recognition. Valid values for LINEAR16 and FLAC are 1-8. Valid values for OGG_OPUS are '1'-'254'. Valid value for MULAW, AMR, AMR_WB and SPEEX_WITH_HEADER_BYTE is only 1. If 0 or omitted, defaults to one channel (mono)."
          },
          "enableSeparateRecognitionPerChannel": {
            "type": "boolean",
            "x-zen-display-name": "Enable Separate Recognition Per Channel",
            "description": "This needs to be set to true explicitly and audioChannelCount > 1 to get each channel recognized separately."
          },
          "languageCode": {
            "type": "string",
            "x-zen-display-name": "BCP-47 Language Code",
            "description": "Required. The language of the supplied audio as a BCP-47 language tag."
          },
          "alternativeLanguageCodes": {
            "type": "array",
            "x-zen-display-name": "Alternative Language Codes",
            "description": "A list of up to 3 additional BCP-47 language codes, listing possible alternative languages of the supplied audio.",
            "items": {
              "type": "string",
              "x-zen-display-name": "BCP-47 Language Code"
            }
          },
          "maxAlternatives": {
            "type": "integer",
            "x-zen-display-name": "Max Alternatives",
            "description": "Maximum number of recognition hypotheses to be returned."
          },
          "profanityFilter": {
            "type": "boolean",
            "x-zen-display-name": "Profanity Filter",
            "description": "If set to true, the server will attempt to filter out profanities, replacing all but the initial character in each filtered word with asterisks, e.g. 'f***'."
          }
        }
      },
      "RecognitionAudio": {
        "type": "object",
        "description": "Contains audio data in the encoding specified in the RecognitionConfig. Either content or uri must be supplied. Supplying both or neither returns google.rpc.Code.INVALID_ARGUMENT",
        "properties": {
          "content": {
            "type": "string",
            "x-zen-display-name": "Audio File",
            "description": "The audio data bytes encoded as specified in RecognitionConfig. Note: as with all bytes fields, proto buffers use a pure binary representation, whereas JSON representations use base64."
          },
          "uri": {
            "type": "string",
            "x-zen-display-name": "Google Cloud Storage URI",
            "description": "URI that points to a file that contains audio data bytes as specified in RecognitionConfig. The file must not be compressed (for example, gzip). Currently, only Google Cloud Storage URIs are supported, which must be specified in the following format: gs://bucketName/object_name"
          }
        }
      },
      "SpeechRecognitionResult": {
        "type": "object",
        "properties": {
          "alternatives": {
            "type": "array",
            "x-zen-array-index": 0,
            "items": {
              "type": "object",
              "properties": {
                "transcript": {
                  "x-zen-display-name": "Transcript",
                  "type": "string"
                },
                "confidence": {
                  "x-zen-display-name": "Confidence",
                  "type": "number"
                },
                "words": {
                  "x-zen-display-name": "Words",
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "startTime": {
                        "type": "string"
                      },
                      "endTime": {
                        "type": "string"
                      },
                      "word": {
                        "type": "string"
                      },
                      "speakerTag": {
                        "type": "integer"
                      }
                    }
                  }
                }
              }
            }
          },
          "channelTag": {
            "x-zen-display-name": "Channel Tag",
            "type": "integer"
          }
        }
      }
    },
    "securitySchemes": {
      "speech_api": {
        "type": "oauth2",
        "description": "Speech to Text OAuth2 security",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://accounts.google.com/o/oauth2/v2/auth",
            "tokenUrl": "https://oauth2.googleapis.com/token",
            "refreshUrl": "https://oauth2.googleapis.com/token",
            "scopes": {
              "https://www.googleapis.com/auth/cloud-platform": "Access cloud platform"
            }
          }
        }
      }
    }
  }
}

Last updated