When building a platform on StackOne, you can dynamically discover what actions are available for each linked account. This enables you to build custom UIs that adapt to each provider’s capabilities.
This guide is for platform builders creating custom interfaces that display available integrations and actions to their end-users.
Instead of hardcoding integration capabilities, query the /actions endpoint to dynamically determine what’s available.
List All Available Actions
Use GET /actions to retrieve action metadata:
TypeScript SDK
cURL
Python
import { StackOne } from "@stackone/stackone-client-ts";
const stackOne = new StackOne({
security: {
username: process.env.STACKONE_API_KEY!,
password: "",
},
});
const result = await stackOne.actions.listActionsMeta({});
// Result contains providers with their available actions
// result.actionsMetaPaginated?.data = [
// { key: "bamboohr", name: "BambooHR", actions: [...] },
// { key: "salesforce", name: "Salesforce", actions: [...] }
// ]
curl "https://api.stackone.com/actions" \
-u "$STACKONE_API_KEY:"
import requests
import base64
api_key = "v1.eu1.xxxxx"
headers = {
"Authorization": f"Basic {base64.b64encode(f'{api_key}:'.encode()).decode()}"
}
response = requests.get(
"https://api.stackone.com/actions",
headers=headers
)
providers = response.json()["data"]
Filter Actions by Context
Filter by connector, account, or action key to get relevant actions for your UI:
const result = await stackOne.actions.listActionsMeta({
filter: {
connectors: "bamboohr,workday",
accountIds: "acct_abc123",
},
});
curl "https://api.stackone.com/actions?filter[connectors]=bamboohr&filter[account_ids]=acct_abc123" \
-u "$STACKONE_API_KEY:"
Understanding Action Schema
The response is grouped by provider, with each provider containing its available actions:
{
"data": [
{
"key": "sapsuccessfactors",
"name": "SAP SuccessFactors",
"description": "SAP SuccessFactors is a comprehensive cloud-based HCM solution...",
"icon": "https://stackone-logos.com/api/sap-sucessfactors/filled/png",
"version": "1.0.0",
"categories": ["hris", "ats", "lms", "iam"],
"authentication": [
{
"type": "custom",
"label": "OAuth 2.0",
"key": "custom"
}
],
"actions": [
{
"id": "sapsuccessfactors_list_employees",
"label": "List Employees",
"description": "Retrieve a list of all employees...",
"schema_type": "native",
"authentication": [...]
},
{
"id": "sapsuccessfactors_get_employee",
"label": "Get Employee",
"description": "Retrieve detailed information about a specific employee"
}
]
}
],
"next": "eyJyIjp7InAiOjI1fX0..."
}
Provider Fields
| Field | Description |
|---|
key | Provider identifier (e.g., sapsuccessfactors, workday) |
name | Display name for UI |
description | Provider description |
icon | URL to provider logo |
version | Provider integration version |
categories | Categories this provider supports (e.g., hris, ats, lms) |
authentication | Supported authentication methods |
actions | List of available actions |
Action Fields
| Field | Description |
|---|
id | Action identifier for RPC calls |
label | Human-readable label for UI |
description | Action description |
schema_type | Schema type (e.g., native) |
authentication | Authentication methods for this specific action |
operation_details | Detailed operation schema (when include=action_details) |
Building a Dynamic Action UI
Here’s how to build an interface that adapts to available actions:
import { StackOne } from "@stackone/stackone-client-ts";
const stackOne = new StackOne({
security: {
username: process.env.STACKONE_API_KEY!,
password: "",
},
});
async function getActionsForAccounts(accountIds: string[]) {
const result = await stackOne.actions.listActionsMeta({
filter: {
accountIds: accountIds.join(","),
},
});
return result.actionsMetaPaginated?.data ?? [];
}
// Flatten all actions across providers for display
function getAllActions(providers: Awaited<ReturnType<typeof getActionsForAccounts>>) {
return providers.flatMap(provider =>
(provider.actions ?? []).map(action => ({
...action,
providerKey: provider.key,
providerName: provider.name,
}))
);
}
// Group actions by tag for UI sections
function groupByTag(actions: ReturnType<typeof getAllActions>) {
const grouped: Record<string, typeof actions> = {};
for (const action of actions) {
for (const tag of action.tags ?? []) {
grouped[tag] = grouped[tag] || [];
grouped[tag].push(action);
}
}
return grouped;
}
from collections import defaultdict
import requests
import base64
import os
def get_actions_for_accounts(account_ids: list[str]) -> list[dict]:
api_key = os.environ["STACKONE_API_KEY"]
headers = {
"Authorization": f"Basic {base64.b64encode(f'{api_key}:'.encode()).decode()}"
}
response = requests.get(
"https://api.stackone.com/actions",
params={"filter[account_ids]": ",".join(account_ids)},
headers=headers
)
return response.json()["data"]
def get_all_actions(providers: list[dict]) -> list[dict]:
"""Flatten all actions across providers."""
actions = []
for provider in providers:
for action in provider.get("actions", []):
actions.append({
**action,
"provider_key": provider.get("key"),
"provider_name": provider.get("name"),
})
return actions
def group_by_tag(actions: list[dict]) -> dict[str, list[dict]]:
"""Group actions by their tags."""
grouped = defaultdict(list)
for action in actions:
for tag in action.get("tags", []):
grouped[tag].append(action)
return dict(grouped)
Execute Actions via RPC
Once a user selects an action, execute it with the RPC endpoint:
const result = await stackOne.actions.rpcAction({
action: "bamboohr_list_employees",
query: {
pageSize: "25",
},
xAccountId: "customer-bamboohr",
});
curl -X POST "https://api.stackone.com/actions/rpc" \
-u "$STACKONE_API_KEY:" \
-H "x-account-id: customer-bamboohr" \
-H "Content-Type: application/json" \
-d '{
"action": "bamboohr_list_employees",
"query": {
"page_size": 25
}
}'
The action field matches the id from the actions metadata.
Real-World Example: Integration Dashboard
import { StackOne } from "@stackone/stackone-client-ts";
const stackOne = new StackOne({
security: {
username: process.env.STACKONE_API_KEY!,
password: "",
},
});
async function renderIntegrationDashboard(customerId: string) {
// 1. Get customer's linked accounts
const { linkedAccounts } = await stackOne.accounts.listLinkedAccounts({
originOwnerId: customerId,
});
// 2. Get available actions for these accounts
const accountIds = (linkedAccounts ?? []).map(a => a.id).filter(Boolean);
const actionsResult = await stackOne.actions.listActionsMeta({
filter: {
accountIds: accountIds.join(","),
},
});
const providers = actionsResult.actionsMetaPaginated?.data ?? [];
// 3. Build dashboard data with actions grouped by tag
return {
accounts: linkedAccounts,
providers: providers.map(provider => ({
key: provider.key,
name: provider.name,
icon: provider.icon,
actionsByTag: groupByTag(provider.actions ?? []),
totalActions: provider.actions?.length ?? 0,
})),
};
}
function groupByTag(actions: { tags?: string[] }[]) {
const grouped: Record<string, typeof actions> = {};
for (const action of actions) {
for (const tag of action.tags ?? []) {
grouped[tag] = grouped[tag] || [];
grouped[tag].push(action);
}
}
return grouped;
}