Write actions
How to add an approval flow to your MDK app
Overview
This guide demonstrates how to submit approval-gated write actions from a React app, review the server-side voting queue, and approve, reject, or cancel pending actions through the App Node.
Prerequisites
- The App Node is running with auth enabled
- The signed-in user has the App Node
actions:wpermission - The signed-in user also has the target device-family write permissions required by the action, such as
miner:worcontainer:w - The React app is wrapped in
<MdkProvider apiBaseUrl={...}> - The feature stages write actions in
actionsStorefrom@tetherto/mdk-ui-coreor provides actions through an existing feature such as Pool Manager
Submit staged actions
1.1 Submit a single action
Use useSubmitSingleAction() when the UI lets an operator submit one staged action by id.
import { useSubmitSingleAction } from "@tetherto/mdk-react-adapter/hooks";
function SubmitActionButton({ actionId }: { actionId: number }) {
const submit = useSubmitSingleAction();
return (
<button
type="button"
disabled={!submit.canSubmit || submit.submittingActionId === actionId}
onClick={() => submit.submitSingle(actionId)}
>
Submit action
</button>
);
}1.2 Submit all staged actions
Use useSubmitPendingActions() when the UI has a review tray or bulk-submit control that should send the whole local staging queue.
import { useSubmitPendingActions } from "@tetherto/mdk-react-adapter/hooks";
function SubmitActionsButton() {
const submitPending = useSubmitPendingActions();
return (
<button
type="button"
disabled={submitPending.isSubmitting || !submitPending.canSubmit}
onClick={() => submitPending.submit()}
>
Submit staged actions
</button>
);
}Review the server-side queue
After submission, actions move from the local staging queue into the App Node /auth/actions* voting surface.
2.1 Review with usePendingActions()
Use usePendingActions() for a pending-action review table. Pass refetchInterval to override the default poll cadence (see hook reference).
import { usePendingActions } from "@tetherto/mdk-react-adapter/hooks";
function PendingActionsList() {
const { data: pending = [], isLoading } = usePendingActions({
refetchInterval: 5000,
});
if (isLoading) return <p>Loading pending actions...</p>;
return (
<ul>
{pending.map((action) => (
<li key={action.id}>{action.id}</li>
))}
</ul>
);
}2.2 Review with useLiveActions()
Use useLiveActions() when the UI needs to separate the current user's actions from others and gate approve/reject controls on canApprove.
For polling cadence and role logic, see the hook reference.
Approve or reject an action
Use useVoteOnAction() to cast an approval or rejection. The hook calls PUT /auth/actions/voting/:id/vote and invalidates
the relevant action caches. Disable direct vote buttons when canVote is false. Review-tray UIs that approve other users'
actions should combine this mutation with useLiveActions().canApprove.
import { useVoteOnAction } from "@tetherto/mdk-react-adapter/hooks";
function VoteButtons({ actionId }: { actionId: string }) {
const vote = useVoteOnAction();
return (
<>
<button
type="button"
disabled={!vote.canVote}
onClick={() => vote.vote({ id: actionId, approve: true })}
>
Approve
</button>
<button
type="button"
disabled={!vote.canVote}
onClick={() => vote.vote({ id: actionId, approve: false })}
>
Reject
</button>
</>
);
}Cancel pending actions
Use useCancelAction() when the current operator should withdraw one or more pending actions before the vote thresholds are met.
The App Node exposes the voting cancel route at DELETE /auth/actions/voting/cancel.
import { useCancelAction } from "@tetherto/mdk-react-adapter/hooks";
function CancelActionButton({ actionId }: { actionId: string }) {
const cancel = useCancelAction();
return (
<button type="button" onClick={() => cancel.cancel({ ids: [actionId] })}>
Cancel
</button>
);
}Verify the result
Approved actions become command requests after the configured vote thresholds are met. Watch the feature state that initiated the
action, or poll the action list with usePendingActions() / useLiveActions() until the item leaves the voting queue.
For Pool Manager screens, use the existing actions sidebar USAGE and Pool Manager blueprint as the integration examples.
Next steps
- Understand the approval-gated write architecture — including how approved actions become normal command requests
- Understand plugin permission syntax, auth, permissions, and caching
- Configure route permissions in App Node plugins
- Review hook exports in
@tetherto/mdk-react-adapter - Run integration coverage:
backend/core/ork/tests/integration/actions.test.js