Stevora

Quickstart

Run a durable AI workflow with human approval

Quickstart

This example handles a duplicate-charge request:

  1. Gemini drafts a reply.
  2. Stevora saves the result and pauses.
  3. A person approves the reply.
  4. 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/sdk

Add your Stevora key

export STEVORA_API_KEY=stv_your_key

Keep this key on your server. Do not expose it in browser code.

Create and run the workflow

Create run.mjs:

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.mjs

The 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?

CredentialRequired?Purpose
Stevora API keyYesIdentifies your workspace and authorizes SDK requests
Stevora API URLYesUse https://api.stevora.in for the hosted service
OpenAI or Gemini keyNot for the hosted demoStevora currently supplies the hosted model provider

For customer deployments, provider keys can be configured per workspace so model usage stays isolated.

Next steps