Actions

Register app handlers for Widget action buttons.

Last reviewed

Actions

Use a custom action when an Action button under End of flow should call code in your app. Custom actions are available for Widget flows, not hosted pages or embeds. They currently work only for surveys with one ending; conditional endings don't support them.

Custom action contract

A custom action registration has four fields:

FieldMeaning
kindAlways "custom".
keyStable name used to select the action in the editor.
versionPositive integer identifying the handler contract.
handlerNo-argument function called when someone selects the action.

Register a custom action

Register the same definition in every app version that can display a Flow using the action.

JavaScript SDK

TypeScriptapp.ts
import { createClient } from "@getuserfeedback/sdk";const client = createClient({apiKey: "YOUR_API_KEY",actions: [{kind: "custom",key: "open-customer-workspace",version: 1,handler: () => openCustomerWorkspace(),}],});

React SDK

TypeScriptapp.tsx
import { GetUserFeedbackProvider } from "@getuserfeedback/react";export function App() {return (<GetUserFeedbackProviderclientOptions={{apiKey: "YOUR_API_KEY",actions: [{kind: "custom",key: "open-customer-workspace",version: 1,handler: () => openCustomerWorkspace(),}],}}><AppRoutes /></GetUserFeedbackProvider>);}

React Native SDK

TypeScriptapp.tsx
import { GetUserFeedbackProvider } from "@getuserfeedback/react-native";export function App() {return (<GetUserFeedbackProviderclientOptions={{apiKey: "YOUR_API_KEY",actions: [{kind: "custom",key: "open-customer-workspace",version: 1,handler: () => openCustomerWorkspace(),}],}}><AppRoutes /></GetUserFeedbackProvider>);}

React Native currently supports custom actions only. It does not support the browser open-url override.

JavaScript snippet

Define the registry before loading the Widget script:

HTMLindex.html
<script>window.__getuserfeedback_actions = [{kind: "custom",key: "open-customer-workspace",version: 1,handler: () => openCustomerWorkspace(),}];</script><scriptasyncsrc="https://cdn.getuserfeedback.com/widget/loader/v4/YOUR_API_KEY/loader.js"data-api-key="YOUR_API_KEY"></script>

The snippet takes one snapshot of this runtime-wide registry before bootstrap. It cannot be configured after bootstrap or per instance.

Find an action in the editor

Discovery is best-effort. After the runtime has observed the definition, open or refresh the survey editor to find it in the Action selector. Select the matching definition, set the button label, and save the flow.

The definition observed by the editor is setup history. Every live app still needs to register the exact definition.

Match the key and version

The Widget requires an exact key-and-version match between the authored action and the registered handler. It never guesses compatibility. Increase version when the meaning or expected behavior of an action changes incompatibly.

In the JavaScript SDK, definitions become fixed when loading starts. In React and React Native, they remain fixed for the provider's mounted lifetime. The snippet takes its fixed snapshot before bootstrap. Matching handler functions can be refreshed where the platform supports it.

Handler outcomes

The handler receives no arguments. Core waits up to 10 seconds for its result. Resolving during that window reports success, closes the Widget, and can emit Flow Action Succeeded.

Throwing or rejecting reports failure. If the handler is still pending after 10 seconds, the Widget stops waiting: the response stays saved, the acknowledgment stays open, the button is disabled, and the Widget does not retry automatically.

The deadline does not cancel your handler. Its work can still finish, and a later resolution can still emit Flow Action Succeeded, but it does not retroactively close the timed-out Widget. The event records that the handler settled successfully; it does not confirm that a later external effect completed.

Missing actions

Before display, an automatic Widget flow that requires a missing or mismatched action is suppressed. An explicit flow(flowId).open() request rejects instead of rendering an incomplete flow.

Handle browser URL actions

The JavaScript and React SDKs, and the JavaScript snippet, can give your app router the first chance to handle authored webpage actions. Register one open-url action alongside custom actions:

actions: [{kind: "open-url",handler: ({ url, target }) => {if (target !== "self" || !canRouteInApp(url)) return "unhandled";void navigateInApp(url);return "handled";},}]

target is "self" for the current tab and "blank" for a new tab. It can be undefined when the disposition is unavailable. Return "handled" only after your router has synchronously accepted or committed the navigation command. Return "unhandled" to use the browser fallback.

A thrown handler or invalid result reports failure and suppresses the fallback to avoid an unexpected redirect. A later asynchronous router or destination load failure does not retract Flow Action Succeeded after navigation was accepted.