Unable to make secure API calls in FDK v3 app

MY GOAL

I am developing an app for Freshservice using FDK v3, with the goal of creating a graphical interface that, upon clicking a button, triggers a function that retrieves data via HTTP API calls from Freshservice and processes the data.

MY APP STRUCTURE (used template: common-starter-template)
├── app
│ ├── index.html
│ ├── scripts
│ │ ├── app.js
│ │ ├── services/softwareFilterService.js
│ │ └── views/softwareFilterView.js
│ └── functions/softwareFilterFunction.js
├── config/iparams.json
└── manifest.json

  1. scripts/ contains the front-end logic (view + service).
  2. functions/ contains the serverless function that is supposed to make secure calls to Freshservice.
  3. manifest.json is configured according to FDK v3 without a “functions” section.

MY ISSUE

I wrote the serverless function using module.exports = async function(args, context) and client.request.invoke("platform.get/post") as indicated in the FDK v3 documentation.

On the front-end, I use:

const res = await client.request.invoke("softwareFilterFunction", { serial, ticket });

When I try to run the app (even with fdk run), the CLI gets stuck on this line and returns:

✖ Error: Invalid feature or feature not enabled.

OBJECTIVE

To make my app work securely, meaning without exposing the API Key on the front-end, while still being able to make calls to Freshservice.

Currently, without an external server and without serverless functions enabled, I don’t see a way to implement a secure backend.

Can anyone help me? Thanks in advance

Hi Riccardo,
Welcome to the freshworks developer community, great first post!

That “Invalid feature or feature not enabled” error usually means the app isn’t set up the way FDK v3 expects for server methods. A few tweaks should fix it:

1. Use server.js at the project root (not a functions/ folder)
In FDK v3, server methods that you call with client.request.invoke("methodName", payload) must be defined in a root-level server.js, not in files under app/functions/.

So move your logic from app/functions/softwareFilterFunction.js into server.js at the project root, and export a named method that uses renderData(null, data) (or renderData(error)) to send the response. The signature is different from the old module.exports = async function(args, context) pattern — in v3 it’s something like:

// server.js (at project root)
exports = {
  softwareFilterFunction: function (options) {
    // Your logic here; call Freshservice via request method
    renderData(null, { ... });
  }
};

Docs: Create Serverless App

2. Allowlist the method in the manifest
For client.request.invoke("softwareFilterFunction", { serial, ticket }) to work, the manifest must declare that function under modules.common.functions. If your manifest has no functions section, add one and list every server method you invoke from the client, for example:

"modules": {
  "common": {
    "location": { ... },
    "functions": {
      "softwareFilterFunction": {}
    }
  },
  ...
}

3. secure API calls using iparams
Store your Freshservice API key (and any other secrets) as installation parameters (iparams) so they are never in front-end code. Configure iparams in config/iparams.json: give each parameter a display_name, type (e.g. text), and set “secure”: true for sensitive values like API keys.
Those values are then collected on the Settings/Installation page when the app is installed; once saved, secure iparams are masked (e.g. ********) and are not readable in the UI or via client.iparams.get() on the front-end.

In your server method in server.js, the serverless environment receives iparams in the payload (e.g. in options). Use that data with the Request method (request templates) to call Freshservice. Only the server-side request method can use secure iparams (e.g. in request headers); the key never goes to the client.
For full details on defining iparams, building the installation page, secure iparams, and retrieval methods, see: Build an installation page

After making these changes, try again with fdk run, and proceed with the installation page on http://localhost:10001/custom_configs to configure api keys securely.

If you face any issues, let us know, we’re happy to help!

Rohan Kokkula,
Developer Relations at Freshworks

The issue is resolved, thank you very much for your help!