useIntegrationAction() reference.
Actions let a widget call a declared server handler directly—no assistant, no natural language—with input validation and fixed error codes. The harness serves these handlers locally in both fixtures and live mode.
Overview
useIntegrationAction() from @ekanos/sdk/hooks makes a real fetch() to POST /api/integrations/<slug>/actions/<actionName>. The harness route answers it by running your declared handler against a mock (fixtures) or live context.
actions record, keyed by a kebab-case name:
theme key must be declared in your definition’s storage schemas.
Fixtures mode
In fixtures mode, the route validates the posted input against the action’sinput schema, then runs the handler against a mock context built with createMockContext() from your definition and triggerMocks, one per integration and fixture variant.
ctx.fetch is answered from triggerMocks.fetchHandlers first, then from the active variant’s fixtures, and never the real network. The context lives in dev-server memory, so state accumulates across action calls on the same variant; each variant has its own context.
Live mode
In live mode, the route runs the handler against the same server-held context that OAuth and live webhooks/schedules use—itsctx.fetch reaches your real vendor under the declared egress, with admin credentials from .ekanos/local-secrets.json and whatever onTokens or the activation flow stored.
Live mode requires your entry’s live block. An integration with no live block stays on fixtures for actions.
Session confirmation
The first action call on an integration while the toolbar is set to Live API raises a blocking browser confirmation before anything is sent:sessionStorage, so it survives a reload and ends when the tab closes). Declining throws LiveActionNotConfirmedError and the request is never sent. Fixtures mode never prompts.
This is a harness convenience against accidental clicks during iteration, not
a production security boundary. Production runs exactly one mode, with no
toggle and no per-session confirmation.
Response codes
The route answers the same fixedcodes as the production host route. useIntegrationAction()’s rejected mutation surfaces them as an IntegrationActionError (exported from @ekanos/sdk/hooks) with message, status, code, and issues?:
Handler errors
If your handler throws, the route returnshandler_error with the generic message Action failed.. In production, a handler that throws PublicActionError has its own message returned verbatim under handler_error instead:
The harness route always answers a thrown handler with
Action failed.,
including for PublicActionError. For debugging it adds a dev-only devError
field (message and stack) to the response body and prints the real error in
the terminal running ekanos dev; production never returns devError.Example: widget with actions
Troubleshooting
action_not_found
The action name doesn’t match any key in your definition’sactions. Check the second argument to useIntegrationAction().
invalid_input
The input failed theinput schema. The error’s issues array holds the zod issues.
handler_error
The handler threw, or its result failed theoutput schema. In the harness, read devError in the response body or the ekanos dev terminal for the real error.
Live mode confirmation appears again
The confirmation is per integration and per browser tab session. A new tab, or a different integration, asks again.Every action returns 500 handler_error in fixtures mode (older harness)
Before@ekanos/harness 0.4.0, a registry that declared a host-route fixture such as GET /api/integrations/<slug>/storage made every action call fail with handler_error before the handler ran, because the server-side context tried to compile that fixture. 0.4.0 skips host-route fixtures on the action path (an action’s ctx.fetch can only address a vendor origin anyway). Upgrade with ekanos upgrade.
No recorded response error in fixtures mode
A handler’sctx.fetch call matched no triggerMocks.fetchHandlers entry and no fixture for the active variant. Record a fixture (see Fixtures).
Action works in fixtures but fails in live
Check the Local OAuth panel or.ekanos/local-secrets.json for missing admin secrets, and the terminal running ekanos dev for the handler’s error.