TypeScript SDK Reference
The official TypeScript SDK for Verity. Zero dependencies. Works in Node.js 18+ and any runtime with the Fetch API.
npm install @verityinc/sdkVerityClient
The main entry point. Create one instance and reuse it throughout your application.
import { VerityClient } from '@verityinc/sdk';
const verity = new VerityClient({
baseUrl: 'https://api.useverity.io/v1',
apiKey: process.env.VERITY_API_KEY!,
namespace: 'payments',
});Configuration Options
| Option | Type | Default | Description |
|---|---|---|---|
baseUrl | string | required | Verity API base URL |
apiKey | string | required | API key (vt_live_* or vt_test_*) |
namespace | string | undefined | Default namespace for all protect() calls |
agentId | string | undefined | Default agent/worker identifier attached to all requests |
autoRenew | boolean | true | Automatically renew leases in the background while act() runs |
renewAtFraction | number | 0.65 | Fraction of lease duration at which renewal fires (0.3–0.9) |
requestTimeoutMs | number | 20000 | Timeout for individual HTTP requests in ms |
conflictRetry | ConflictRetryConfig | See below | Retry behavior when another agent holds the lease |
onUnavailable | 'proceed' | 'block' | 'block' | What to do when the Verity API is unreachable (network error, 5xx).'block' throws the error (your code stops).'proceed' skips protection and runs act() directly. Only applies to transient errors — deliberate rejections always throw. |
logger | VerityLogger | console | Custom logger with warn, error, and optional debug/info |
onUnavailable: 'proceed' means your code always runs even if Verity is down, but you lose the exactly-once guarantee for that invocation. Use 'block' (default) when correctness matters more than availability (e.g. payments), and 'proceed' when availability matters more (e.g. notifications).ConflictRetryConfig
Controls automatic retry when another agent holds the lease (HTTP 409):
| Option | Type | Default | Description |
|---|---|---|---|
enabled | boolean | true | Enable automatic retry on 409 |
maxAttempts | number | 12 | Maximum retry attempts |
initialDelayMs | number | 500 | Initial delay between retries in ms |
maxDelayMs | number | 15000 | Maximum delay between retries in ms |
jitter | boolean | true | Add ±30% random jitter to delays (prevents thundering herd) |
protect()
Protect a standalone effect. This is the primary API for single actions.
const result = await verity.protect<RefundResult>(effectKey, options, params?);Parameters
| Parameter | Type | Description |
|---|---|---|
effectKey | string | Unique key identifying this effect instance (your idempotency key) |
options | ProtectOptions<T> | observe (optional) + act (required) callbacks |
params | ProtectParams | Optional overrides (namespace, agentId, leaseDuration, etc.) |
ProtectOptions<T>
| Field | Type | Description |
|---|---|---|
observe | () => Promise<T | null | undefined> | Check external system for an already-completed action. Called only when priorState === 'expired'. |
act | () => Promise<T> | Execute the real-world side effect. Required. |
ProtectParams
| Field | Type | Default | Description |
|---|---|---|---|
namespace | string | Client default | Namespace override for this call |
effectName | string | undefined | Human-readable name (e.g., "stripe.charge") |
agentId | string | Client default | Agent identifier for this request |
leaseDurationMs | number | 30000 | Lease duration (5,000–120,000 ms) |
inputJson | unknown | undefined | Input data for debugging/replay (max 64 KB) |
onConflict | 'retry' | 'throw' | 'retry' | 'throw' — throw LeaseConflictError immediately on 409 |
onLease | (lease) => void | undefined | Callback after lease is acquired (for logging/metrics) |
keySuffix | string | undefined | Appended to effectKey for cardinality (e.g., recipient email) |
Return Value
Returns Promise<T> — the result from either act(), observe(), or the cached result from a prior commit.
Example
const refund = await verity.protect('refund:order_48392', {
observe: async () => {
const existing = await stripe.refunds.list({ charge: chargeId, limit: 1 });
return existing.data.length > 0 ? existing.data[0] : null;
},
act: async () => {
return await stripe.refunds.create({ charge: chargeId, amount: 4999 });
},
}, {
effectName: 'stripe.refund',
leaseDurationMs: 60_000, // 60s for slow Stripe calls
inputJson: { orderId, chargeId, amount: 4999 },
});Workflow Builder
For multi-effect processes, use the fluent workflow builder:
const run = verity.workflow(workflowName).case(caseId).run(options?);
await run.protect(effectName, options, overrides?);workflow(workflowName)
Returns a WorkflowContext bound to the workflow name. The workflow name is used as the default namespace and for grouping in the Explorer UI.
.case(caseId)
Returns a CaseContext bound to a specific case (business entity / customer intent). The case ID becomes part of the effect key: "${caseId}:${effectName}".
.run(options?)
Returns a RunContext — an execution attempt for the case. Options:
| Option | Type | Default | Description |
|---|---|---|---|
runId | string | Auto-generated | Explicit run ID for tracing |
namespace | string | workflowName | Namespace override |
leaseDurationMs | number | 30000 | Default lease duration for all effects in this run |
agentId | string | undefined | Agent identifier for all effects in this run |
run.protect(effectName, options, overrides?)
Protect an effect within the workflow run. The effect key is automatically derived as "${caseId}:${effectName}", ensuring idempotency across runs.
The overrides object accepts the same fields as ProtectParams plus:
namespace— per-effect namespace override (for cross-namespace workflows)keySuffix— appended for cardinality:"${caseId}:${effectName}:${keySuffix}"
Example
const run = verity.workflow('refund_flow').case('order_123').run();
await run.protect('validate_order', {
act: () => orderService.validate(orderId),
});
await run.protect('process_refund', {
observe: () => checkExistingRefund(chargeId),
act: () => stripe.refunds.create({ charge: chargeId }),
});
// Send to multiple recipients in the same workflow
for (const recipient of recipients) {
await run.protect('notify', {
act: () => emailService.send({ to: recipient }),
}, { keySuffix: recipient }); // effectKey = "order_123:notify:alice@example.com"
}Low-Level API
For power users who need manual control over the lease lifecycle. These methods are public on VerityClient:
requestLease(namespace, body)
const lease = await verity.requestLease('payments', {
effectKey: 'refund:order_123',
effectName: 'stripe.refund',
agentId: 'worker-1',
leaseDurationMs: 30_000,
});commit(namespace, body)
await verity.commit('payments', {
effectKey: 'refund:order_123',
fenceToken: lease.fenceToken,
leaseToken: lease.leaseToken,
result: { refundId: 're_123' },
source: 'acted',
});fail(namespace, body)
await verity.fail('payments', {
effectKey: 'refund:order_123',
fenceToken: lease.fenceToken,
leaseToken: lease.leaseToken,
error: { message: 'Stripe declined' },
source: 'acted',
});renew(namespace, body)
await verity.renew('payments', {
effectKey: 'refund:order_123',
fenceToken: lease.fenceToken,
leaseToken: lease.leaseToken,
extensionMs: 30_000,
});Error Classes
All errors extend VerityError. See Error Handling for complete details, including when each error is thrown and how to handle them.
| Error | When |
|---|---|
VerityApiError | Non-2xx response from Verity API |
LeaseConflictError | Another agent holds the lease (409, after retries exhausted) |
EffectPreviouslyFailedError | Effect already failed — admin reset required |
CommitUncertainError | Action succeeded but commit couldn't be confirmed — critical |
VerityConfigError | Missing required configuration (baseUrl, apiKey, namespace) |
VerityValidationError | Input data not JSON-serializable or exceeds 64 KB |
Exports
// Classes
import {
VerityClient,
WorkflowContext,
CaseContext,
RunContext,
} from '@verityinc/sdk';
// Errors
import {
VerityError,
VerityApiError,
VerityConfigError,
VerityValidationError,
LeaseConflictError,
EffectPreviouslyFailedError,
CommitUncertainError,
} from '@verityinc/sdk';
// Types
import type {
VerityConfig,
ConflictRetryConfig,
VerityLogger,
ProtectOptions,
ProtectParams,
WorkflowRunOptions,
LeaseResponse,
LeaseGrantedResponse,
LeaseCachedResponse,
CommitResponse,
FailResponse,
RenewResponse,
LeaseRequestBody,
CommitRequestBody,
FailRequestBody,
RenewRequestBody,
} from '@verityinc/sdk';