Verity
/Docs

Quickstart

Protect your first real-world action in under 5 minutes. This guide covers installation, configuration, and your first protect() call — in both TypeScript and Python.

Prerequisites

  • A Verity account with an API key (starts with vt_live_ or vt_test_)
  • Node.js 18+ (TypeScript) or Python 3.10+ (Python)
Test vs Live keysvt_test_ keys write to an isolated test environment. Use them during development. vt_live_ keys write to production. Data between environments is completely separated via row-level security.

1. Install the SDK

TypeScript

npm install @verityinc/sdk

Or with your preferred package manager:

pnpm add @verityinc/sdk
yarn add @verityinc/sdk

Python

pip install verityinc-sdk

2. Initialize the Client

TypeScript

import { VerityClient } from '@verityinc/sdk';

const verity = new VerityClient({
  baseUrl: 'https://api.useverity.io/v1',
  apiKey:  process.env.VERITY_API_KEY!,   // vt_live_xxxxxxxx or vt_test_xxxxxxxx
  namespace: 'payments',                   // logical grouping for your effects
});

Python

from verity import VerityClient

verity = VerityClient(
    base_url="https://api.useverity.io/v1",
    api_key=os.environ["VERITY_API_KEY"],   # vt_live_xxxxxxxx or vt_test_xxxxxxxx
    namespace="payments",                    # logical grouping for your effects
)

3. Protect Your First Action

Wrap any external side effect in verity.protect(). The first argument is a unique effect key — a string that identifies this specific action instance.

TypeScript

// Protect a Stripe refund — safe to retry, exactly once
const refund = await verity.protect('refund:order_48392', {
  observe: async () => {
    // Check if refund already happened (crash recovery)
    const existing = await stripe.refunds.list({ charge: chargeId, limit: 1 });
    return existing.data.length > 0 ? existing.data[0] : null;
  },
  act: async () => {
    // Execute the actual refund
    return await stripe.refunds.create({
      charge: chargeId,
      amount: 4999,
      reason: 'requested_by_customer',
    });
  },
});

console.log('Refund ID:', refund.id);

Python

# Protect a Stripe refund — safe to retry, exactly once
refund = await verity.protect(
    "refund:order_48392",
    observe=check_existing_refund,    # Check if refund already happened
    act=execute_refund,               # Execute the actual refund
)

print(f"Refund ID: {refund['id']}")

What Just Happened?

Behind a single protect() call, Verity executed this protocol:

  1. Lease — acquired an exclusive lock on refund:order_48392 using a monotonically increasing fence token. No other agent can touch this effect.
  2. Observe — since this is a fresh effect (priorState: 'none'), the observe step was skipped. If a prior attempt had crashed, Verity would have called your observe() function to check Stripe before acting again.
  3. Act — your act() function ran, creating the refund in Stripe.
  4. Commit — the refund result was recorded in Verity's ledger. Any future call with the same effect key returns the cached result instantly — no duplicate refund.

The entire flow is visible in the Explorer UI as an audit trail with timestamps, fence tokens, and agent identifiers.

Simpler: Without Observe

If your action is naturally idempotent (or you don't need crash recovery), you can skip the observe callback:

// TypeScript — just act()
const result = await verity.protect('send-welcome:user_42', {
  act: () => emailService.send({ to: user.email, template: 'welcome' }),
});
# Python — just act()
result = await verity.protect(
    "send-welcome:user_42",
    act=lambda: email_service.send(to=user.email, template="welcome"),
)

Without observe, Verity still gives you exactly-once guarantees via leasing and fence tokens. If your agent retries, Verity returns the cached result from the first successful execution.

When to add observe — use it when your action has real-world cost (money, infrastructure, notifications) and a prior attempt might have succeeded before crashing. The observe function lets you check the external system before acting again.

Protect Multiple Actions (Workflows)

For multi-step processes, use the workflow builder to group effects under a case:

// TypeScript — multi-effect workflow
const run = verity.workflow('refund_flow').case('order_123').run();

// Each effect gets a deterministic key: "order_123:validate_order"
await run.protect('validate_order', {
  act: () => orderService.validate(orderId),
});

await run.protect('process_refund', {
  observe: () => checkExistingRefund(chargeId),
  act: () => stripe.refunds.create({ charge: chargeId }),
});

await run.protect('notify_customer', {
  act: () => emailService.send({ to: email, template: 'refund_complete' }),
});

Each effect in the workflow is independently protected. If the agent crashes after the refund but before the email, a re-run will skip the completed refund (cached) and only send the email. See Workflows for full details.

Next Steps