Pular para o conteúdo principal

Custom Actions

Most agent behavior is covered by the built-in action types: navigate, form_fill, ui_interaction, scroll_list, and confirm. But real apps have buttons that do more than move between screens — they call APIs, start payments, delete records. This guide shows how to expose that logic to the agent without giving up control.

The core pattern: the agent presses, your handler decides​

The agent never runs your business logic directly. It presses a registered target, and your own onPress handler does whatever it always did. That means exposing custom behavior is mostly about registering the trigger and describing it well:

import { useAppilotsTarget } from '@appilots/sdk';

function SubscriptionScreen() {
const renewSubscription = async () => {
await api.renewSubscription(); // your logic, unchanged
};

useAppilotsTarget('renewSubscription', {
onPress: renewSubscription,
label: 'Renew subscription',
screen: 'Subscription',
});

return <Button title="Renew" onPress={renewSubscription} />;
}

From the agent's perspective this is a ui_interaction press. From yours, it's the same code path a human tap uses — validation, error handling, and side effects all stay in one place.

Describe the action's intent and risk​

Pressing "Renew subscription" charges money. The agent should never do that silently. Declare the action's semantics in registerScreen so a confirmation gate is inserted before it runs:

import { registerScreen } from '@appilots/sdk';

registerScreen({
name: 'Subscription',
title: 'Subscription',
actions: [
{
id: 'renewSubscription',
label: 'Renew subscription',
type: 'button',
effect: 'write',
riskLevel: 'high',
requiresConfirmation: true,
},
{
id: 'cancelSubscription',
label: 'Cancel subscription',
type: 'button',
effect: 'destructive',
riskLevel: 'high',
destructive: true,
},
],
});

With this metadata in place, the user sees an explicit confirmation card in the chat before either action executes — and confirm steps always require a real tap, regardless of the chat's autoExecute setting. The SDK enforces this locally from your registerScreen metadata even if the uploaded MCP document is stale, as a safety net.

The CLI's static analysis also detects many of these on its own: handler names with mutation verbs (delete..., pay..., renew...) and type: 'submit' actions are flagged as mutating, and the metadata lint warns when a mutating action is missing effect/riskLevel. Run appilots sync --strict-metadata in CI to make those warnings blocking.

When the custom action type appears​

The wire-level action type custom exists for actions that don't map onto the built-in payloads. In practice you rarely need to think about it: the recommended integration path is always "register a target + describe it" as above, because it keeps your app in control of what actually executes. If you're consuming the API directly (no SDK), treat custom actions as opaque { id, type, payload } envelopes: execute them however your client sees fit and report the outcome via /agent/continue.

Checklist​

  • Register the trigger (useAppilotsTarget, a HOC, or auto-tracking with a testID).
  • Give it a clear, unambiguous label — that's what the agent reasons about.
  • Declare effect + riskLevel (and destructive/requiresConfirmation where applicable) in registerScreen.
  • Re-run appilots sync so the MCP document reflects the new action.
  • Test the risky path: ask the agent to perform the action and verify the confirmation card appears before anything happens.