Use Run SDK and Workflow SDK to execute dynamic JavaScript with an approval step before a refund. Run SDK runs the code with explicitly provided host functions, and Workflow SDK saves its continuation while waiting for the user's decision.
The demo uses Hono for API routes and Nitro with workflow/nitro to run the server. Workflow SDK persists pending runs locally across restarts.
Copy link to headingHow the two SDKs work together
Each SDK owns one side of the problem:
| Concern | Run SDK | Workflow SDK |
|---|---|---|
| Execution | Runs the dynamic source in a sandbox with no access to the filesystem, environment variables, application modules, or network | Runs trusted application code as durable steps |
| State | Returns a signed continuation when the source is interrupted | Saves step results and stores the continuation between rounds |
| Waiting | Interrupts before a protected action and returns immediately | Suspends at an approval hook and resumes when a decision arrives |
When the dynamic source calls the protected orders.refund() function, the two SDKs hand off to each other:
- Run SDK interrupts before the action and returns a signed continuation.
- Workflow SDK stores the continuation, suspends the workflow at an approval hook, and resumes when a decision arrives.
- Run SDK replays the source, restores completed host-function calls from the continuation, and continues at the refund request.
Copy link to headingPrerequisites
You will need:
- Node.js 22.13 or later
- pnpm
Copy link to heading1. Create the Hono and Nitro application
Install the versions used by the linked example, including Workflow SDK 4.8.5 and Nitro 3 beta. Keep these versions together for this tutorial; Workflow SDK 5 changes the hook metadata API used by the copied handlers.
Update the scripts and module type in package.json:
Keep the dependency fields created by the pnpm add commands; the snippet above shows only the project fields you need to add or update.
Create tsconfig.json with the Workflow SDK TypeScript plugin:
Create nitro.config.ts to load Workflow SDK and route requests through Hono:
Create a signing secret for Run SDK continuations:
Add the generated value to .env:
Create the directories used in the rest of the guide:
Copy link to headingProject structure
The example keeps each execution boundary in its own directory:
Each directory maps to one responsibility:
| Directory | Responsibility |
|---|---|
src/workflow/ | Durable orchestration: loops through sandbox rounds and waits on approval |
src/steps/ | Retryable server work that runs the sandbox and returns serializable results |
src/sandbox/ | The dynamic source, the runner configuration, and the host functions it may call |
src/api/ | Hono routes that start automations, report status, and resume workflows |
src/domain/ | The order store and the serializable types shared across boundaries |
public/ | The browser interface, which never touches either SDK |
Copy link to heading2. Define the dynamic refund code
Save this example source string in src/sandbox/example-source.ts:
You can change the condition or refund amount before starting a run. Regardless of where the string came from, Run SDK treats it as dynamic source rather than trusted application code.
Copy link to heading3. Configure the runner
Configure signing and sandbox limits in src/sandbox/create-order-runner.ts:
Each option controls one part of the continuation contract:
| Option | Purpose |
|---|---|
continuationAudience | Ties continuations to this application version. Keep it stable for as long as a continuation may be resumed. |
continuationCodec | Signs continuations so they can't be forged or altered. maxAgeMs makes each token expire 30 days after issuance; resuming an expired token fails. |
limits | Caps execution time and memory for each sandbox run. |
Token expiry does not cancel the workflow's pending approval hook.
Pending continuations need the same signing secret and audience. Run SDK also checks the source, host-function names, and continuation context before replay.
Copy link to heading4. Expose the order capabilities
The dynamic source calls orders.get() and orders.refund(). The host application implements and explicitly passes these functions into the sandbox.
Create src/sandbox/order-host-functions.ts with host functions that validate input and interrupt before issuing a refund:
context.interrupt() stops the sandbox before the refund is sent. When the run resumes, the same host function receives the approval as context.resume.resolution and validates its shape before acting on it.
The refund still uses an idempotency key because a workflow step can be retried after an external service accepts a request, but before the step result is saved.
The example's src/domain/order-store.ts provides the small local order adapter used by these host functions. Add it alongside src/domain/types.ts, which defines AutomationScope and the other serializable messages passed between Run SDK, Workflow SDK, and the API.
Copy link to heading5. Execute Run SDK inside a durable step
Create src/steps/run-automation-round.ts from the complete implementation below. It builds the runner inside a durable step and returns serializable outcomes for completion, interruption, or failure.
Each call to runAutomationRound() is one durable step. The initial call has no continuation, while each later call receives the continuation and a resolution for every pending interruption.
Copy link to heading6. Wait for an approval decision
Create src/workflow/order-automation-workflow.ts. It repeats Run SDK rounds until the source completes or returns a failure:
At await approval, the workflow suspends without keeping a server process waiting in memory. After a decision arrives, dispose of that hook before starting the next sandbox round so another protected action can open a new approval round.
createRunResolutions() in src/domain/types.ts matches each decision in the approval batch to its pending interruption. It rejects missing, duplicate, or unknown decisions and returns the { interruptionId, value } objects Run SDK expects.
Add src/steps/publish-approval-request.ts for local approval notifications. This demo logs the hook token to the terminal; remove that logging when replacing it with a production notification adapter.
Copy link to heading7. Add the Hono API
The Hono application exposes four routes used by the UI:
| Route | Method | Purpose |
|---|---|---|
/api/example-source | GET | Returns the preloaded dynamic source for the editor |
/api/automations | POST | Validates the source and starts the workflow |
/api/automations/:automationId | GET | Reports the running, waiting, or completed state |
/api/automations/:automationId/decision | POST | Verifies the decision and resumes the approval hook |
Create src/app.ts to connect these routes to the API handlers and return JSON errors. The local demo also exposes /api/orders/stats for inspecting store calls:
startAutomation() starts orderAutomationWorkflow with start() from workflow/api. getAutomation() reads the workflow and hook state for the UI. submitDecision() verifies the approval belongs to the same automation before calling resumeHook().
Create the API handlers from the example:
src/api/start-automation.tsvalidates the source and starts the workflow.src/api/get-automation.tsreturns the running, waiting, or completed state.src/api/submit-decision.tsvalidates the decision and resumes the approval.src/api/auth.ts,automation-identity.ts, andread-bounded-json.tsprovide the demo's request boundary and signed automation identifiers.
The example uses an x-demo-role header as a local substitute for authentication. Replace it with your application's session and authorization checks before using this pattern in production.
Copy link to heading8. Connect the browser interface
The UI is made from three static files:
public/index.htmldefines the markup for the source editor, approval panel, result panel, and run timeline.public/app.jsstarts the automation, polls its status, renders approval requests, and submits decisions.public/styles.csslays out the two panels and styles each workflow state.
Create the files under public. The browser calls the Hono API and uses the automation and workflow run IDs to reconnect. Both SDKs run on the server, which keeps approval hook tokens and Run SDK continuations.
Copy link to heading9. Try the approval flow
Check the assembled project, then start the development server:
Test the approval flow:
- Open localhost:3000.
- Review the preloaded source and select Run automation.
- Wait for the refund approval card to appear.
- Choose Approve or Reject, then select Submit decision.
- Confirm that the workflow completes. The dynamic result is
completeafter approval orrefund_rejectedafter rejection.
Copy link to heading10. Test the restart behavior
In development, Workflow SDK persists workflow state in the .workflow-data directory, which is what allows a run to survive a restart.
Test resumption across a server restart:
- Start an automation and leave it waiting for approval.
- Stop the development server.
- Start it again from the same project directory, keeping
.workflow-dataand the signing secret unchanged. - Reload the same browser URL.
- Approve or reject the refund and confirm that the workflow completes.
Workflow SDK restores the workflow and pending hook. Run SDK replays orders.get() from the continuation's recorded data without another store call.
Check the terminal output: it should show orders.get before the wait, with no second read after resumption. An approval logs orders.refund once; a rejection does not issue a refund.
Copy link to headingProduction considerations
The included order store and role headers are designed for a local demonstration. For a production implementation:
- Authenticate users before starting, reading, or approving an automation.
- Check tenant and workflow ownership before resuming a hook.
- Validate every argument received by a host function.
- Use transactional writes and provider idempotency keys for refunds.
- Keep hook tokens and Run SDK continuations on the server and out of logs.
- Keep the source, host-function names, continuation context, audience, and signing configuration stable during resumption.
Signed Run SDK continuations provide integrity, not encryption. Do not put secrets or sensitive customer data in dynamic source, host-function results, interruption payloads, or continuation context.
Copy link to headingNext steps
- Review the durable order automation example.
- Learn more about Workflow SDK.
- Read the Run SDK announcement.
- For workloads that need an operating system, package installation, or process-level isolation, use Edgenivo Sandbox.