Request method is a secure mechanism to send HTTP requests from an app to a third-party domain, without exposing any sensitive information that is part of the request. Through browsers, app users may intercept sensitive information such as API keys or user credentials. Request method safeguards against such exposure. Typically, HTTP requests are routed through proxy servers. In such cases, sending the HTTP requests through the request method helps identify the origin in scenarios where the third-party domain has enabled Cross Origin Resource Sharing (CORS) support.
Note: This Request Method is supported only on FDK version 9.0.0 or later and platform version 2.3. If you are on platform version 2.2 (scheduled for deprecation on July 31, 2023) and building an app that uses the Request Method, see Request Method with domain whitelisting.
- As part of the app configuration, configure request templates.
- In manifest.json, declare the template(s) that the app code uses.
- From the app logic, invoke the configured template(s), and make HTTP requests to third-party domains.
Notes:
1. The default request timeout is five seconds. For the local testing of apps that use the request method, you can specify an appropriate timeout. For information on local testing with REQUEST_TIMEOUT, see Test apps that use request method.
2. The rate limit for requests is 50 requests per minute per app per account.
Configure request templates
Request templates are snapshots of all the HTTP requests that an app is expected to make.
- From the app’s root directory, navigate to the config folder and create a requests.json file (if requests.json does not exist).
- In requests.json, use the following syntax to configure all request templates that the app is expected to use, as JSON objects.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | { "<requestTemplateName>": { "schema": { // ... request schema }, "options": { // ... request options } }, "<requestTemplateName1>": { "schema": { // ... request schema }, "options": { // ... request options } } } |
FILE ATTRIBUTE | DATA TYPE | DESCRIPTION | ||
---|---|---|---|---|
<requestTemplateName> | object. | <requestTemplateName> is the name used at runtime to invoke the request template. This object contains the attributes that are required to build the request. |
||
schema MANDATORY Child attribute of <requestTemplateName> |
object |
Properties of HTTP requests and the corresponding values specified as key-value pairs of Note: The HTTP request body is not part of the template configuration/schema. It is passed as part of the template invocation. Example: Copied Copy
|
||
Child attributes of the schema object Note: Some of these attribute values can contain variables that can be filled at runtime. For more information, see template substitutions. |
||||
method MANDATORY |
string | HTTP method. Valid values: GET, POST, PUT, DELETE, PATCH |
||
protocol | string | If specified, the value must be HTTPS. You can use HTTP during local testing of the app.
Ensure that the app submitted for review does not contain a protocol value of HTTP. Default value: HTTPS (if no protocol value is specified) |
||
host MANDATORY |
string | Absolute domain name of the domain to which the app sends the HTTP request. Must not be an IP address; must be a Fully Qualified Domain Name. Ensure not to specify the protocol nor to append a trailing slash as part of the host value. | ||
path | string |
Path to the resource on the host domain. Ensure to construct the path with a leading slash. Default value: / (if no path value is specified) |
||
query | object |
Query parameters and the corresponding values, specified as key-value pairs of <queryParameter-name>:<queryParameter-value>. The query parameters are used in conjunction with the path parameters when constructing the HTTP request. Example: Copied Copy
|
||
headers | object | Valid HTTP headers (such as Authorization, Content-type, and so on) and the corresponding values, specified as key-value pairs of <header-name>:<header-value>. | ||
|
||||
options Child attribute of <requestTemplateName> |
object | Optional parameters and the corresponding values that help to make a successful HTTP request call. The options are specified as key-value pairs of <optional-parameter-name>:<optional-parameter-value>.
Example:
Copied
Copy
|
||
Child attributes of the options object | ||||
maxAttempts | number |
Maximum number of times that a request can be resent if a network or 429/5xx HTTP error occurs. Valid values: 1, 2, 3, 4, 5 Default value: 1 |
||
retryDelay | number |
Time in milliseconds after which a request can be resent. Valid values: 1 through 1500 Default value: 1000 |
||
isOAuth | boolean |
Specifies whether the request template is used to make a OAuth request. Valid values: false true: If isOAuth is true, the access token for OAuth authorisation must be specified as a variable in schema.headers.Authorization. Example of access_token usage: Copied Copy
As part of our OAuth offering, when app users Authorize an app to place a OAuth request, the access token is stored in .fdk/localstore (for account level authorization) or the browser's localStorage (for agent level authorization). When the request call is placed, the access_token variable used in the Authorization value is populated with the stored access token. For more information, see OAuth. Default value: false |
Template substitutions
Template substitutions enable the usage of variables in the <requestTemplateName>.schema attributes. During runtime, depending on where the variables are used, they can be populated by: Variable limitationsVariables can be used only in the following <requestTemplateName>.schema attributes. Also, the type of data that can populate the variables varies depending on the attribute in which the variables are used.
<requestTemplateName>.schema attributes | Type of data that can populate the variables |
---|---|
Notes: For a detailed description of these schema attributes, see the Attribute description table.
|
|
host | Non-secure iparam values |
path |
|
headers (can be used only in <header-value>)> |
|
query |
|
Substitute template variables with iparam values
Use the following syntax to declare a template variable that is populated with iparam values at runtime.
Copied Copy1 | <%= iparam.validIparamName %> |
1 2 3 4 5 6 7 8 | "schema": { "method": "GET", "host": "<%= iparam.domain %>.myfreshworks.com", "path": "/crm/sales/api/contacts/view/[:view_id]", "headers": { "Authorization": "Bearer <%= iparam.api_key %>", "Content-Type": "application/json" }} |
encode()
This function enables encoding secure iparams that are passed as part of the request.
Syntax: <%= encode(iparam.validIparamName) %>
Example: "Authorization": "Bearer <%= encode(iparam.api_key) + ':x' %>"
Substitute template variables with contextual data
Use the following syntax to declare a template variable that is populated with contextual data, passed through the runtime API that invokes the request template.
Copied Copy1 | <%= context.variableName %> |
Example: Using a pagination variable in the <requestTemplateName>.schema.query attribute Copied Copy
1 2 3 4 5 6 7 8 9 10 11 | "schema": { "method": "GET", "host": "<%= iparam.domain %>.myfreshworks.com", "path": "/crm/sales/api/contacts/view/[:view_id]", "headers": { "Authorization": "Bearer <%= iparam.api_key %>", "Content-Type": "application/json" }} "query": { "page": "<%= context.page %>" } |
Substitute template variables with access tokens
Use the following syntax to declare a template variable that is populated with an access token value.
Copied Copy
1 | <%= access_token %> |
1 2 3 4 5 6 7 8 9 10 11 12 | "schema": { "method": "GET", "host": "app.asana.com", "path": "/api/1.0/workspaces", "headers": { "Authorization": "bearer <%= access_token %>", "Content-Type": "application/json" } }, "options": { "isOAuth": true } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | { "getContacts": { "schema": { "method": "GET", "host": "<%= iparam.domain %>.myfreshworks.com", "path": "/crm/sales/api/contacts/view/[:view_id]", "headers": { "Authorization": "Bearer <%= iparam.apikey %>", "Content-Type": "application/json" }, "query": { "page": "<%= context.page %>" } }, "options": { "retryDelay": 1000 } }, "sendToExternalAPI": { "schema": { "method": "POST", "host": "<%= iparam.ext_domain %>.example.com", "path": "/api/", "headers": { "Authorization": "Bearer <%= iparam.ext_apikey %>", "Content-Type": "application/json" } } } } |
Declare templates
From templates configured in the config/requests.json, in manifest.json, declare the ones that the app code uses, for a specific product.- From the app’s root directory, navigate to the manifest.json file.
- Under product.<productName>, use the following syntax and create a requests attribute.
Copied
Copy
12345
"requests": { "<requestTemplateName>":{}, "<requestTemplateName1>": {} … }
Note: Ensure that the <requestTemplateName> is the same as that configured in config/requests.json. If there is a mismatch, an error message is displayed.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | { "platform-version": "2.3", "product": { "freshworks_crm": { "events": { "onContactCreate": { "handler": "onContactCreateHandler" } }, "requests": { "createContact": {}, "getContacts": {} } } }, "engines": { "node": "18.12.1", "fdk": "9.0.0" } } |
Invoke templates
To send an HTTP request to a third-party domain, in the app code, include the invokeTemplate() method. This method serves as a runtime API call. As part of the call, the app can pass a dynamic context and any other data that the template requires.General syntax of invokeTemplate()
For front-end apps,-
In the front-end HTML files (such as iparams.html), include the client JS resource as follows:
Copied
Copy
1
<script src="{{{appclient}}}"></script> Note: If your existing app uses Request method with domain whitelisting and if you are migrating your app to use the templated request method, in the front-end HTML files (such as iparams.html), replace the existing client JS resource (<script src="https://static.freshdev.io/fdk/2.0/assets/fresh_client.js"> </script>) with <script src="{{{appclient}}}"></script>
-
Navigate to the app.js file and include the invokeTemplate() method using the following syntax.
Copied
Copy
1234
client.request.invokeTemplate(requestTemplateName, { context: {}, body: JSON.stringify(body), });
1 2 3 4 | $request.invokeTemplate(requestTemplateName, { context: {}, body: JSON.stringify(body), }); |
invokeTemplate() arguments
- requestTemplateName (MANDATORY, string): Identifier of the request template in config/requests.json and the request in manifest.json. The requestTemplateName must be the same in the app code, configuration (config/requests.json), and declaration (manifest.json).
-
An object with the following optional attributes:
- context (object): All contextual data that the template requires, specified as key-value pairs of <variableName>:<variableValue>.<variableName> must be the same as that used in config/requests.json to receive contextual data.
- body (string): Request body.
Response caching: Requests made through the request method are governed by rate limits. To prevent repetitive calls from breaching the rate limits, the request method comes with an opt-in response caching feature, for front-end apps. The cache and ttl options available as part of the invokeTemplate() method, enable browsers to cache the responses that are retrieved from third-party calls. The responses are cached in the Browser’s local storage. For requests that haven’t changed since the last call, the response is retrieved from the cache. This conserves the app’s rate limits as the number of requests made to the third-party are conserved. - cache (boolean): This option is valid only for front-end apps and can be used only with client.request.invokeTemplate().
cache specifies whether a successful response to the HTTP request is cached in the browser’s local storage. The cached response persists in the local storage for a time specified by the ttl value.
If no ttl is specified, the response is cached for the default ttl value of 60000 milliseconds.
Possible values: true, false - ttl (number): This option is valid only for front-end apps, when cache is true, and can be used only with client.request.invokeTemplate().
If cache is true, ttl (time to live) is the duration (specified in milliseconds) for which the response is stored in the cache.
Default value: 60000
Response
If the runtime API call results in placing a successful request call to the third-party domain, a data object similar to the following sample is returned. Copied Copy1 2 3 4 5 6 7 8 9 | { "status" : 200, "headers": { "Content-Length": 20, "Content-Type": "application/json;charset=utf-8" }, "response": ‘{ "content": "Test message"}’ } |
Attribute Name | DATA TYPE | DESCRIPTION |
---|---|---|
status | number | HTTP status code. |
headers | object | Additional context about the response, received as an object of valid <headerParameterName> :< headerParameterValue> pairs. |
response | string | Data retrieved successfully from the third-party domain, through the request method, specified as a string. |
1 2 3 4 5 6 7 8 9 | { "status" : 502, "headers": { "Content-Type": "application/json;charset=utf-8" }, "response": "Error in establishing the connection.", "errorSource": "PLATFORM" } |
Note: If the maxAttempts option is specified either in config/requests.json or manifest.json, the app attempts the HTTP request for the number of times specified. After all attempts have been exhausted, if the call still fails, the error response is returned.
Attribute Name | DATA TYPE | DESCRIPTION |
---|---|---|
status | number | HTTP status code. |
headers | object | Additional context about the response, received as an object of valid <headerParameterName> :< headerParameterValue> pairs. |
response | string | Message specifying the reason for the error. |
errorSource | string | Specifies whether the error is app or platform related. Possible values: APP, PLATFORM |
Sample app.js
Copied Copy1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | // ------ // Setup // ------ document.onreadystatechange = function () { if (document.readyState === "interactive") { app .initialized() .then(function (client) { client.events.on("app.activated", function () { onAppActivate(client); }); }) .catch(handleErr); } }; // <-- additional app code --> // ------ // App Logic // ------ async function onAppActivate(client) { // Get messages for page 2 await client.request.invokeTemplate("getContacts", { context: { page: 2, }, }); } |
Sample app.js for caching responses
Copied Copy1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | // ------ // Setup // ------ document.onreadystatechange = function () { if (document.readyState === "interactive") { app .initialized() .then(function (client) { client.events.on("app.activated", function () { onAppActivate(client); }); }) .catch(handleErr); } }; // <-- additional app code --> // ------ // App Logic // ------ async function onAppActivate(client) { // Get contacts for page 2 await client.request.invokeTemplate("getContacts", { context: {page: 2}, cache: true, ttl: 70000 }); } |
Sample server.js
Copied Copy1 2 3 4 5 6 7 8 | exports = { async onMessageCreateHandler(args) { // Send a copy of the newly created message to the external API await $request.invokeTemplate("sendToExternalAPI", { body: JSON.stringify(args.data), }); }, }; |
Test apps that use request method
In the local testing of apps that use the Request Method to make third-party HTTP requests, you can specify a value for the request timeout.
To configure the request timeout to a suitable value, perform one of the following:- Use the REQUEST_TIMEOUT parameter along with the fdk run command as follows: Syntax REQUEST_TIMEOUT=<timeout in milliseconds> fdk run Example: REQUEST_TIMEOUT=10000 fdk run
- Navigate to the .env file and add the following: export REQUEST_TIMEOUT=<timeout in milliseconds> Example: export REQUEST_TIMEOUT=10000
- If you specify a REQUEST_TIMEOUT value that breaches the min or max limits, the timeout is defaulted to the min or max value and a warning message is displayed.
- If you don’t specify a value for REQUEST_TIMEOUT, the timeout value is 5 seconds.
For information on how to test an app that uses the Request API feature, see Test your App.
Request method with domain whitelisting
Important: This request method is supported only for building apps with platform version 2.2 and FDK versions prior to 9.0.0. Support for building apps on platform version 2.2 is scheduled for deprecation on July 31, 2023. Ensure to migrate to the latest platform and FDK versions. If you are on the latest FDK version (platform 2.3), to build apps that use the request method, see Request Method.
In this request method, the hosts to which the apps are expected to make request calls are whitelisted in manifest.json. The other attributes such as methods, paths, query parameters, header parameters, options, and request body are dynamically generated during runtime.
1. The default request timeout is five seconds. For the local testing of apps that use the request method, you can specify an appropriate timeout. For information on local testing with REQUEST_TIMEOUT, see Test apps that use request method.
2. The rate limit for requests is 50 requests per minute per app per account.
Usage
The Request method should be defined in the app.js file by using the following syntax.
Copied Copy1 2 3 4 5 6 7 8 9 10 | client.request.get("URL", options) .then( function(data) { //handle "data" //"data" is a json string with status, headers, and response. }, function(error) { //handle failure } ); |
The Request method should be defined in the server.js file by using the following syntax.
Copied Copy1 2 3 4 5 6 7 8 9 10 | $request.get("URL", options) .then( function(data) { //handle "data" //"data" is a json string with status, headers, and response. }, function(error) { //handle failure } ); |
The following table lists the parameters and their description.
PARAMETER | DESCRIPTION |
---|---|
URL | Is mandatory to make the Request method call. |
options |
Is a JSON object and can include the following properties:
|
data | Data received from the Request method if the request is successful. |
error | Message received from the request if an error is encountered. |
Using Iparams
Iparams can be used in requests as part of the following:
- Header
- URL
Copied
Copy
1
https://passport-office.com/user?id=<%= iparam.passport %> - Body
Copied
Copy
EXPAND ↓123456789101112client.request.post("https://helloworld.myfreshworks.com/api/contacts", { headers: { Authorization: "Basic <%= encode(iparam.username + ':' + iparam.password) %>" }, body: JSON.stringify({ status: 2, priority: 1, description: "Test", subject: "<%= iparam.subjectPrefix %> Hello there", email: "tom@outerspace.com" }) });
Note:
Secure iparams can only be used in the request header. If secure iparams are present in the request body or URL, the fdk run command displays an error message Secure iparam cannot be used in request body or URL.
Sample requests
Request method should be defined in the app.js file, you can use the following sample requests for reference.
1. GET request with authentication Copied Copy1 2 3 4 5 6 7 8 9 10 11 | var headers = {"Authorization": "Token token=<%= iparam.api_key %>"}; var options = { headers: headers }; var url = "https://sample.myfreshworks.com/itil/requesters/5.json"; client.request.get(url, options) .then ( function(data) { console.log(data); }, function(error) { console.log(error); }); |
If the request is successful, data is returned in the following format. In case a network or 429/5xx HTTP error occurs, the app will retry the request a number of times to obtain the response. Here, attempts is the number of attempts after which the request has returned a response.
Copied Copy1 2 3 4 5 6 7 8 9 10 | { status : 200, headers: { "Content-Length": 20, "Content-Type": "application/json;charset=utf-8" }, response: "{ "Name": "Rachel"}", "attempts": 1 } |
If the request fails, an error response is returned in the following format.
Copied Copy1 2 3 4 5 6 7 8 9 | { status : 401, headers: { "Content-Type": "application/json;charset=utf-8" }, response: [{"message": "Session expired or invalid", "attempts": 1, "errorCode": "INVALID_SESSION_ID"}] } |
If the request fails due to an error from the app, the error response is returned in the following format.
Copied Copy1 2 3 4 5 6 7 8 9 10 | { "status" : 400, "headers": { "Content-Type": "application/json;charset=utf-8" }, "response": "This domain has not been whitelisted.", "attempts": 2, "errorSource": "APP" } |
2. GET request without authentication Copied Copy
1 2 3 4 5 6 7 8 9 10 | var options = {}; var url = "https://httpbin.org/get?arg1=hello_world"; client.request.get(url, options) .then ( function(data) { console.log(data); }, function(error) { console.log(error); }); |
3. POST request with authentication Copied Copy
1 2 3 4 5 6 7 8 9 10 11 | var headers = {"Authorization": "Token token=<%= iparam.api_key %>"}; var options = { headers: headers, body: "Hello world"}; var url = "https://sample.myfreshworks.com/api/contacts"; client.request.post(url, options) .then ( function(data) { console.log(data); }, function(error) { console.log(error); }); |
For information on how to make an OAuth request, see Usage in the OAuth section.
Testing
For information on how to test an app that uses the Request method feature, see Test apps that use request method.IP Ranges
Here is the list of IPs you must whitelist when using Request APIs if IP whitelisting is enabled on the Freshdesk support portal or you wish to whitelist requests from your app.
Region | IPs |
---|---|
United States |
18.233.117.211 and 35.168.222.30 |
Europe-Central | 18.197.138.225 and 52.57.69.21 |
India | 13.232.159.149 and 13.233.170.242 |
Australia | 13.211.182.225 and 52.63.187.64 |
Errors
In case of request failure, a status code is displayed along with a message to help troubleshoot the issue.
An example of a status code and message is as follows.
Copied Copy1 2 3 4 5 6 7 8 9 10 11 | { "status": 401, "headers": { "Content-Type": "text/plain" }, "response": [{ "message": "Session expired or invalid", "errorCode": "INVALID_SESSION_ID" }] } |
The following table lists all the supported status code.
STATUS | DESCRIPTION |
---|---|
400 | Is returned due to invalid input. For example, if you are making a request to a domain that has not been whitelisted or if the request is passing secure iparams in the body or URL, you will receive this status. |
401 | Is returned if you performed an unauthorized request. |
429 | Is returned when the number of requests exceeds the threshold. |
500 | Is returned if the server encountered an unexpected error. If this error persists, contact us at support@freshworks.com. |
502 | Is returned when there is a network error. If this error persists, contact us at support@freshworks.com. |
503 | Is returned when the service is temporarily unavailable. |
504 | Is returned when there is a timeout error while processing the request. |