> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stackone.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Actions Metadata for Custom UIs

> Build dynamic interfaces by querying available actions, parameters, and provider capabilities

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.

<Info>
  This guide is for **platform builders** creating custom interfaces that display available integrations and actions to their end-users.
</Info>

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:

<Tabs>
  <Tab title="TypeScript SDK">
    ```typescript theme={null}
    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: [...] }
    // ]
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={null}
    curl "https://api.stackone.com/actions" \
      -u "$STACKONE_API_KEY:"
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    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"]
    ```
  </Tab>
</Tabs>

***

## Filter Actions by Context

Filter by connector, account, or action key to get relevant actions for your UI:

<Tabs>
  <Tab title="TypeScript SDK">
    ```typescript theme={null}
    const result = await stackOne.actions.listActionsMeta({
      filter: {
        connectors: "bamboohr,workday",
        accountIds: "acct_abc123",
      },
    });
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={null}
    curl "https://api.stackone.com/actions?filter[connectors]=bamboohr&filter[account_ids]=acct_abc123" \
      -u "$STACKONE_API_KEY:"
    ```
  </Tab>
</Tabs>

<Tip>
  See the [List Actions API Reference](/platform/api-reference/actions/list-all-actions-metadata) for all available filter parameters.
</Tip>

***

## Understanding Action Schema

The response is grouped by provider, with each provider containing its available actions:

```json theme={null}
{
  "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:

<Tabs>
  <Tab title="TypeScript SDK">
    ```typescript theme={null}
    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;
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    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)
    ```
  </Tab>
</Tabs>

***

## Execute Actions via RPC

Once a user selects an action, execute it with the RPC endpoint:

<Tabs>
  <Tab title="TypeScript SDK">
    ```typescript theme={null}
    const result = await stackOne.actions.rpcAction({
      action: "bamboohr_list_employees",
      query: {
        pageSize: "25",
      },
      xAccountId: "customer-bamboohr",
    });
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={null}
    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
        }
      }'
    ```
  </Tab>
</Tabs>

<Tip>
  The `action` field matches the `id` from the actions metadata.
</Tip>

***

## Real-World Example: Integration Dashboard

```typescript theme={null}
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;
}
```

***

## Related

<CardGroup cols={2}>
  <Card title="Actions RPC" icon="bolt" href="/platform/api-reference/actions/make-an-rpc-call-to-an-action">
    Execute actions programmatically
  </Card>

  <Card title="List Actions API" icon="code" href="/platform/api-reference/actions/list-all-actions-metadata">
    Full API reference for actions metadata
  </Card>

  <Card title="Multi-Tenant Accounts" icon="users" href="/api/multi-tenant-accounts">
    Segment accounts by customer
  </Card>

  <Card title="Connector Metadata" icon="puzzle-piece" href="/platform/api-reference/connectors/list-connector-meta-information">
    Get connector logos and branding
  </Card>
</CardGroup>
