Quickstart
Run a durable AI workflow with human approval
Quickstart
This example handles a duplicate-charge request:
- Gemini drafts a reply.
- Stevora saves the result and pauses.
- A person approves the reply.
- Stevora resumes and completes the run.
It uses the same hosted flow we test in production.
Prerequisites
- Node.js 18+
- A Stevora workspace API key
For access, create an account, then create an API key from Settings. For guided setup, book a demo.
Install the SDK
npm install @stevora/sdkAdd your Stevora key
export STEVORA_API_KEY=stv_your_keyKeep this key on your server. Do not expose it in browser code.
Create and run the workflow
Create run.mjs:
import { AgentRuntime } from '@stevora/sdk';
const stevora = new AgentRuntime({
apiKey: process.env.STEVORA_API_KEY,
baseUrl: 'https://api.stevora.in',
});
const definition = await stevora.definitions.create({
name: 'refund-review',
version: String(Date.now()),
description: 'Draft a customer reply and wait for human approval.',
steps: [
{
type: 'llm',
name: 'draft-response',
model: 'gemini-3.6-flash',
responseFormat: 'json',
maxTokens: 1024,
systemPrompt:
'Return only JSON with keys reply and reason. Keep the reply under 40 words.',
messages: [
{
role: 'user',
content:
'Customer: {{input.message}} Order: {{input.orderId}}. Draft a clear response confirming human review.',
},
],
},
{
type: 'approval',
name: 'human-review',
contentKey: 'reply',
prompt: 'Approve this reply before it is sent.',
},
],
});
const run = await stevora.workflows.create({
definitionId: definition.id,
input: {
orderId: 'ORDER-842',
message: 'I was charged twice. Please refund the duplicate charge.',
},
idempotencyKey: `refund-order-842-${Date.now()}`,
});
console.log(`Run started: ${run.id}`);
let approval;
while (!approval) {
const current = await stevora.workflows.get(run.id);
if (current.status === 'FAILED') {
throw new Error(JSON.stringify(current.error));
}
const pending = await stevora.approvals.list({ pending: true });
approval = pending.find((item) => item.workflowRunId === run.id);
if (!approval) {
await new Promise((resolve) => setTimeout(resolve, 1500));
}
}
console.log('Draft waiting for approval:', approval.content);
// In a real product, call this after a person clicks Approve.
await stevora.approvals.approve(approval.id, 'quickstart-user');
const completed = await stevora.workflows.waitForCompletion(run.id, {
pollIntervalMs: 1000,
timeoutMs: 60000,
});
const cost = await stevora.workflows.getCost(run.id);
console.log(`Final status: ${completed.status}`);
console.log(`LLM cost: $${cost.totalCostDollars}`);Run it
node run.mjsThe run should pause at human-review, resume after approval, and finish as COMPLETED.
What Stevora stores
For this run, Stevora persists:
- the workflow input and current state
- each step and its status
- the approval request and decision
- model, token, latency, and cost records
- the full event timeline
If the worker restarts while the run is waiting, the state remains in PostgreSQL and the workflow can continue.
What credentials are required?
| Credential | Required? | Purpose |
|---|---|---|
| Stevora API key | Yes | Identifies your workspace and authorizes SDK requests |
| Stevora API URL | Yes | Use https://api.stevora.in for the hosted service |
| OpenAI or Gemini key | Not for the hosted demo | Stevora currently supplies the hosted model provider |
For customer deployments, provider keys can be configured per workspace so model usage stays isolated.