Package Exports
- @stacksona/sdk
- @stacksona/sdk/dist/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@stacksona/sdk) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
@stacksona/sdk
TypeScript and JavaScript SDK for Stacksona Gate, the approval and audit layer for AI agents.
@stacksona/sdk lets AI agents log task events, request approval before gated actions, wait for human review, validate signed approval tokens, and safely continue only when Stacksona Gate allows or approves the action.
Use it to add human-in-the-loop approvals, audit logs, and runtime AI governance to your agent workflows with a simple Node.js SDK.
Stacksona AI Observability and Approval Layer
What it does
Stacksona Gate helps agents pause before sensitive actions and route those actions through an approval workflow.
With this SDK, your app or agent can:
- Log agent task events for observability and audit trails
- Request a decision before a gated action
- Automatically wait for human approval or rejection
- Validate signed one-time approval tokens
- Execute approved actions safely
- Track decision status by task or thread
- Add a policy and approval layer to existing agent tools
- Send revision events when a reviewer requests changes to a pending decision
Install
npm install @stacksona/sdkRequirements
- Node.js 18 or later
- TypeScript or JavaScript project
- A Stacksona Gate workspace
- A Stacksona API key
- Your Stacksona Gate URL
Environment variables
STACKSONA_GATE_URL=https://your-gate-subdomain.stacksona.cloud
STACKSONA_API_KEY=sg_your_api_keybaseUrl is required. Do not hard-code the example URL unless that is your actual Stacksona Gate tenant. Gate instances may use different subdomains.
Quick start: gate an action
Use runGatedAction for the normal integration path.
It requests a decision, waits when human review is required, validates signed approval tokens when enabled, and only executes your function when Stacksona Gate allows or approves the action.
import { StacksonaGateClient } from '@stacksona/sdk';
const gate = new StacksonaGateClient({
baseUrl: process.env.STACKSONA_GATE_URL!,
apiKey: process.env.STACKSONA_API_KEY!,
});
await gate.logEvent({
taskId: 'task-abc-123',
eventType: 'task.started',
eventSummary: 'Customer requested refund of $500',
payload: { customer_id: 'cus_99' },
});
const { decision, executed, result } = await gate.runGatedAction(
{
taskId: 'task-abc-123',
workflowName: 'Customer Support',
taskLabel: 'Refund request - Order #8821',
toolName: 'issue_refund',
subject: 'Issue refund of $500 to customer cus_99',
riskLevel: 'high',
summary: ['Order placed 14 days ago', 'No tracking movement'],
payload: { amount: 500, currency: 'usd' },
},
async () => issueRefund({ amount: 500, currency: 'usd', customerId: 'cus_99' }),
{ validateSignedApprovalToken: true },
);
if (!executed) {
console.log(`Action did not run: ${decision.status}`);
}Manual approval flow
Use the lower-level methods when you need custom control over routing, timing, UI state, or execution.
const decision = await gate.requestDecision({
taskId: 'task-abc-123',
workflowName: 'Customer Support',
taskLabel: 'Refund request - Order #8821',
toolName: 'issue_refund',
subject: 'Issue refund of $500 to customer cus_99',
riskLevel: 'high',
payload: { amount: 500, currency: 'usd' },
});
if (decision.status === 'allow') {
await issueRefund();
}
if (decision.status === 'pending_review') {
const finalDecision = await gate.pollDecision({ thread_id: decision.thread_id! });
if (finalDecision.status === 'approved') {
await issueRefund();
}
}Signed approval token validation
Signed approval tokens provide proof that a gated action was approved before execution.
const finalDecision = await gate.pollDecision({ thread_id: 'THR-4F2A9C1B' });
if (finalDecision.status === 'approved' && finalDecision.approval_token) {
const validation = await gate.validateApprovalToken({
taskId: finalDecision.task_id,
signature: finalDecision.approval_token,
});
if (!validation.valid) {
throw new Error(validation.reason ?? 'Approval token invalid');
}
}Main methods
logEvent(input)
Sends timeline context to Stacksona Gate reviewers.
Use this for agent observability, task history, audit logs, and reviewer context.
Revision events
Revision events are a structured use of logEvent that allow an agent to update a pending decision thread when a reviewer has requested changes. Instead of opening a new decision request, the agent sends a revision event on the existing thread — reviewers see the updated proposal and can approve, deny, or request further changes.
When to use revision events
Revision events apply only while the target thread is still awaiting a reviewer decision (needs_review or escalated). Once a thread is approved or denied, that decision is final and cannot be revised in place.
How it works
The reviewer replies in the thread with requested changes. The agent picks up that feedback, adjusts its proposal, and calls logEvent with an event_type of revision.{taskId}.{threadId}. Gate evaluates the revised request_payload through the same tool and rule system as a normal decision request. If the rules allow it, the thread still waits for reviewer confirmation — the agent has updated the request, not bypassed the human loop.
Each new revision supersedes any earlier pending revision on that thread. To avoid stale approvals, Gate blocks approval against older revisions and requires a reviewer to refresh before approving a superseded request.
Event type format
revision.{taskId}.{threadId}The taskId keeps the event tied to the running task. The threadId identifies the exact review thread to update, which matters when a task has more than one active decision thread.
Example
await gate.logEvent({
taskId: 'task-abc-123',
eventType: 'revision.task-abc-123.THR-XXXXXXXX',
eventSummary: 'Agent revised the request after reviewer feedback.',
payload: {
revision: {
revisionId: 'rev-002',
workflowName: 'Customer Support',
taskLabel: 'Refund request — Order #8821',
toolName: 'issue_refund',
subject: 'Approve conditional refund for customer cus_99',
preview: 'Refund releases only after the prepaid return label is scanned.',
riskLevel: 'high',
summary: [
'Reviewer requested a conditional refund',
'Agent revised the proposal to wait for return-label scan',
],
requestPayload: {
amount: 500,
currency: 'usd',
releaseCondition: 'return_label_scanned',
},
},
},
});Revision payload fields
| Field | Description |
|---|---|
revisionId |
Stable identifier for this revision (e.g. rev-001, rev-002). Each revision should increment this. |
workflowName |
Workflow name, same as the original decision request. |
taskLabel |
Human-readable task label shown in the reviewer timeline. |
toolName |
Tool name being requested, same as the original decision request. |
subject |
Updated subject line describing the revised action. |
preview |
Optional human-readable description of the change being proposed. |
riskLevel |
Risk level of the revised request. Re-evaluated through Gate's rule system. |
summary |
Array of strings summarizing the revision context for the reviewer. |
requestPayload |
The updated tool payload to be evaluated through the standard decision rules. |
Handling the revision loop
After sending a revision event, resume polling on the same thread. Gate will update the pending decision with the revised request; no new thread or decision ID is created.
// After receiving reviewer feedback via your application layer:
await gate.logEvent({
taskId: 'task-abc-123',
eventType: 'revision.task-abc-123.THR-XXXXXXXX',
eventSummary: 'Agent revised: added return-label scan condition.',
payload: { revision: { /* ... */ } },
});
// Continue polling the same thread for the final decision
const finalDecision = await gate.pollDecision({ thread_id: 'THR-XXXXXXXX' });
if (finalDecision.status === 'approved') {
await issueConditionalRefund({ releaseCondition: 'return_label_scanned' });
}requestDecision(input)
Asks Stacksona Gate to allow, reject, or create a review thread for a gated action.
Use this before an agent takes a sensitive, risky, expensive, external, or policy-controlled action.
getDecision(query)
Checks the status of a decision by thread_id or task_id.
Use this when you need to refresh approval status from your own UI or workflow.
pollDecision(query, options)
Waits until a pending decision is approved or rejected.
The SDK uses recommended_poll_after_seconds when Stacksona Gate returns it. Otherwise, the fallback polling interval defaults to 15 seconds.
requestDecisionAndPoll(input, options)
Requests a decision and waits automatically when the result is pending_review.
Use this when you want a direct approval flow but still want to control action execution yourself.
validateApprovalToken(input)
Validates one-time approval tokens server-side.
Use this when signed approval tokens are enabled and you need proof that an action was approved before execution.
runGatedAction(input, action, options)
Helper that executes action only after the request is allowed or approved.
Use this for the fastest and safest integration path.
Common use cases
Use @stacksona/sdk when your AI agent needs approval before actions like:
- Sending emails
- Issuing refunds
- Updating customer records
- Calling external APIs
- Running automations
- Deploying code
- Changing account settings
- Accessing sensitive data
- Triggering financial or operational workflows
Field compatibility
The public SDK accepts camelCase fields like:
workflowNametaskLabeltoolNameeventTypeeventSummary
The SDK automatically serializes these fields to the snake_case API wire format.
Older positional forms, such as requestDecision(taskId, input), still work for compatibility.
TypeScript support
@stacksona/sdk is built for TypeScript and JavaScript projects.
It includes typed client methods for logging events, requesting decisions, polling approval status, validating approval tokens, and running gated actions.
Keywords
Stacksona Gate, AI agent approval, TypeScript SDK, JavaScript SDK, human-in-the-loop AI, agent audit logs, AI observability, runtime AI governance, approval workflow, AI policy layer, gated actions, agent safety, decision workflow.
Links
- Website: https://stacksona.com
- npm package:
@stacksona/sdk