Skip to main content

Node.js SDK

FieldValue
Document IDASCEND-SDK-010
Version2.0.0
Last UpdatedDecember 19, 2025
AuthorAscend Engineering Team
PublisherOW-KAI Technologies Inc.
ClassificationEnterprise Client Documentation
ComplianceSOC 2 CC6.1/CC6.2, PCI-DSS 7.1/8.3, HIPAA 164.312, NIST 800-53 AC-2/SI-4

Reading Time: 15 minutes | Skill Level: Intermediate

Overview

The ASCEND Node.js SDK (@ascend/sdk) provides enterprise-grade governance integration for TypeScript and JavaScript AI agents. It includes full TypeScript types, circuit breaker patterns, and fail mode configuration.

Installation

npm install @ascend/sdk
# or
yarn add @ascend/sdk

Requirements

  • Node.js 16+
  • TypeScript 4.5+ (optional but recommended)

Verify Installation

import { VERSION } from '@ascend/sdk';
console.log(`ASCEND SDK Version: ${VERSION}`);

Quick Start

// Source: sdk/nodejs/src/index.ts:22
import { AscendClient, FailMode, Decision } from '@ascend/sdk';

// Initialize client
const client = new AscendClient({
apiKey: process.env.ASCEND_API_KEY!,
agentId: 'my-agent-001',
agentName: 'My AI Agent',
environment: 'production',
failMode: FailMode.CLOSED
});

// Evaluate an action
const decision = await client.evaluateAction({
actionType: 'database.query',
resource: 'production_db',
parameters: { query: 'SELECT * FROM users' }
});

if (decision.decision === Decision.ALLOWED) {
console.log('Action approved!');
// Execute your action here
} else if (decision.decision === Decision.DENIED) {
console.log(`Denied: ${decision.reason}`);
} else if (decision.decision === Decision.PENDING) {
console.log('Awaiting human approval');
}

Client Configuration

Constructor Options

// Source: sdk/python/owkai_sdk/client.py:243 (same pattern in Node.js)
interface AscendClientOptions {
apiKey: string; // Required: Your API key
agentId?: string; // Agent identifier
agentName?: string; // Human-readable name
apiUrl?: string; // API endpoint URL
environment?: string; // production, staging, development
failMode?: FailMode; // CLOSED (block) or OPEN (allow)
timeout?: number; // Request timeout (seconds)
maxRetries?: number; // Max retry attempts
enableCircuitBreaker?: boolean; // Enable circuit breaker
circuitBreakerThreshold?: number; // Failures before opening
circuitBreakerTimeout?: number; // Recovery timeout
signingSecret?: string; // HMAC signing secret
debug?: boolean; // Enable debug logging
}
OptionTypeDefaultDescription
apiKeystringRequiredASCEND API key
agentIdstring-Unique agent identifier
agentNamestring-Human-readable name
apiUrlstringhttps://pilot.owkai.appAPI endpoint
environmentstringproductionDeployment environment
failModeFailModeCLOSEDBehavior when unavailable
timeoutnumber5Request timeout (seconds)
maxRetriesnumber3Max retry attempts
enableCircuitBreakerbooleantrueEnable circuit breaker
debugbooleanfalseEnable debug logging

Environment Variables

export ASCEND_API_KEY="owkai_your_key_here"
export ASCEND_API_URL="https://pilot.owkai.app"
export ASCEND_AGENT_ID="my-agent-001"
export ASCEND_ENVIRONMENT="production"

Fail Mode Configuration

The SDK supports two fail modes for when ASCEND is unreachable:

import { AscendClient, FailMode } from '@ascend/sdk';

// CLOSED (default): Block actions when ASCEND is unavailable
// Recommended for high-security environments
const secureClient = new AscendClient({
apiKey: process.env.ASCEND_API_KEY!,
failMode: FailMode.CLOSED
});

// OPEN: Allow actions when ASCEND is unavailable
// Use only when availability is more important than security
const availableClient = new AscendClient({
apiKey: process.env.ASCEND_API_KEY!,
failMode: FailMode.OPEN
});

Agent Registration

// Source: sdk/python/owkai_sdk/client.py:570 (same pattern)
// Register agent with ASCEND
const registration = await client.register({
agentType: 'supervised',
capabilities: ['data_access', 'file_operations'],
allowedResources: ['production_db', 's3_bucket'],
metadata: {
version: '1.0.0',
team: 'data-engineering'
}
});

console.log(`Registered with trust level: ${registration.trust_level}`);

Evaluating Actions

Basic Evaluation

// Source: sdk/python/owkai_sdk/client.py:644 (same pattern)
const decision = await client.evaluateAction({
actionType: 'database.query',
resource: 'customer_db',
parameters: {
table: 'customers',
operation: 'SELECT',
columns: ['id', 'name', 'email']
},
context: {
sessionId: 'sess_123',
purpose: 'customer_support'
}
});

EvaluateAction Options

OptionTypeRequiredDescription
actionTypestringYesCategory.action format
resourcestringYesResource being accessed
parametersobjectNoAction-specific parameters
contextobjectNoExecution context
resourceIdstringNoSpecific resource ID
riskIndicatorsobjectNoPre-computed risk signals
waitForDecisionbooleanNoWait for approval (default: true)
timeoutnumberNoWait timeout in seconds

Decision Response

interface AuthorizationDecision {
actionId: string; // Unique action identifier
decision: Decision; // ALLOWED, DENIED, PENDING
riskScore: number; // 0-100 risk score
riskLevel: RiskLevel; // LOW, MEDIUM, HIGH, CRITICAL
reason: string; // Explanation
executionAllowed: boolean; // Can proceed
approvalRequestId?: string; // If pending
metadata: Record<string, any>;
}

// Decision enum
enum Decision {
ALLOWED = 'allowed',
DENIED = 'denied',
PENDING = 'pending_approval',
AUTO_APPROVED = 'auto_approved'
}

Waiting for Decisions

Async Wait

// Submit action that may need approval
const initialDecision = await client.evaluateAction({
actionType: 'financial.transfer',
resource: 'banking_api',
parameters: { amount: 50000 }
}, { waitForDecision: false });

if (initialDecision.decision === Decision.PENDING) {
// Wait for human approval
const finalDecision = await client.waitForDecision(
initialDecision.actionId,
60 // 60 second timeout
);

if (finalDecision.decision === Decision.ALLOWED) {
executeTransfer();
}
}

Check Approval Status

// Poll for approval status
const status = await client.checkApproval(approvalRequestId);

if (status.approved) {
console.log(`Approved by: ${status.approver}`);
} else if (status.denied) {
console.log(`Denied: ${status.comments}`);
} else {
console.log('Still pending...');
}

Action Logging

Log Successful Completion

// Source: sdk/python/owkai_sdk/client.py:802 (same pattern)
const decision = await client.evaluateAction({
actionType: 'database.query',
resource: 'customer_db'
});

if (decision.executionAllowed) {
const startTime = Date.now();
try {
const result = await executeQuery();

// Log success (required for SOC 2)
await client.logActionCompleted(decision.actionId, {
result: { rowCount: result.length },
durationMs: Date.now() - startTime
});
} catch (error) {
// Log failure
await client.logActionFailed(decision.actionId, {
error: { code: 'QUERY_ERROR', message: error.message },
durationMs: Date.now() - startTime
});
}
}

Webhook Configuration

// Source: sdk/python/owkai_sdk/client.py:984 (same pattern)
await client.configureWebhook({
url: 'https://your-app.com/webhooks/ascend',
events: [
'action.approved',
'action.denied',
'action.pending',
'policy.violation'
],
secret: 'whsec_your_secret'
});

Webhook Payload Format

{
"event": "action.approved",
"timestamp": "2025-12-16T10:00:00Z",
"data": {
"action_id": "act_123",
"agent_id": "my-agent-001",
"approver": "admin@company.com"
},
"signature": "v1=abc123..."
}

Verify Webhook Signature

import crypto from 'crypto';

function verifyWebhookSignature(
payload: string,
signature: string,
secret: string,
timestamp: string
): boolean {
const message = `${timestamp}.${payload}`;
const expected = crypto
.createHmac('sha256', secret)
.update(message)
.digest('hex');

return crypto.timingSafeEqual(
Buffer.from(`v1=${expected}`),
Buffer.from(signature)
);
}

Circuit Breaker

The SDK includes a circuit breaker to prevent cascade failures:

// Source: sdk/python/owkai_sdk/client.py:62 (same pattern)
import { CircuitBreaker, CircuitState } from '@ascend/sdk';

const client = new AscendClient({
apiKey: process.env.ASCEND_API_KEY!,
enableCircuitBreaker: true,
circuitBreakerThreshold: 5, // Open after 5 failures
circuitBreakerTimeout: 30 // Try to recover after 30s
});

// Circuit breaker states:
// - CLOSED: Normal operation
// - OPEN: Failing, reject requests immediately
// - HALF_OPEN: Testing if service recovered

Error Handling

Exception Types

// Source: sdk/nodejs/src/errors.ts
import {
OWKAIError, // Base error
AuthenticationError, // Invalid API key
AuthorizationError, // Action denied
TimeoutError, // Request timeout
RateLimitError, // 429 response
ValidationError, // Invalid input
ConnectionError, // Network failure
CircuitBreakerOpenError // Circuit breaker open
} from '@ascend/sdk';

Production Error Handling

import {
AscendClient,
AuthenticationError,
ConnectionError,
CircuitBreakerOpenError,
TimeoutError
} from '@ascend/sdk';

async function executeWithGovernance() {
const client = new AscendClient({
apiKey: process.env.ASCEND_API_KEY!,
failMode: FailMode.CLOSED
});

try {
const decision = await client.evaluateAction({
actionType: 'data.access',
resource: 'customer_db'
});

if (decision.executionAllowed) {
return await executeBusinessLogic();
} else {
throw new Error(`Action not allowed: ${decision.reason}`);
}
} catch (error) {
if (error instanceof AuthenticationError) {
console.error('Invalid API key');
// Check ASCEND_API_KEY
} else if (error instanceof CircuitBreakerOpenError) {
console.error('ASCEND service appears down');
// Implement fallback
} else if (error instanceof TimeoutError) {
console.error('Request timed out');
// Consider queuing for retry
} else if (error instanceof ConnectionError) {
console.error('Network error');
// Check connectivity
}
throw error;
}
}

MCP Integration

Govern Model Context Protocol tools:

// Source: sdk/nodejs/src/mcp.ts
import { mcpGovernance, requireGovernance, highRiskAction } from '@ascend/sdk';

// Wrap MCP tool with governance
const governedTool = mcpGovernance({
actionType: 'database.query',
riskLevel: 'high'
})(originalTool);

// Decorator for high-risk actions
const tools = {
execute_sql: highRiskAction(async (params) => {
return await executeSql(params.query);
})
};

Metrics Collection

// Source: sdk/nodejs/src/metrics.ts
import { MetricsCollector } from '@ascend/sdk';

const metrics = new MetricsCollector();

// Get metrics snapshot
const snapshot = metrics.getSnapshot();
console.log(`Total requests: ${snapshot.totalRequests}`);
console.log(`Success rate: ${snapshot.successRate}%`);
console.log(`Avg latency: ${snapshot.avgLatencyMs}ms`);

// Subscribe to metrics
metrics.onMetric((event) => {
console.log(`${event.type}: ${event.duration}ms`);
});

Complete Example

// main.ts - Production ASCEND Integration
import { AscendClient, FailMode, Decision } from '@ascend/sdk';

const client = new AscendClient({
apiKey: process.env.ASCEND_API_KEY!,
agentId: 'production-agent',
agentName: 'Production Data Agent',
environment: 'production',
failMode: FailMode.CLOSED,
enableCircuitBreaker: true
});

async function processCustomerRequest(customerId: string) {
// Test connection
const status = await client.testConnection();
if (status.status !== 'connected') {
throw new Error(`Cannot connect: ${status.error}`);
}

// Evaluate action
const decision = await client.evaluateAction({
actionType: 'database.read',
resource: 'customer_database',
resourceId: customerId,
parameters: {
table: 'customers',
operation: 'SELECT',
dataClassification: 'pii'
}
});

if (decision.executionAllowed) {
const startTime = Date.now();
try {
const data = await fetchCustomerData(customerId);

await client.logActionCompleted(decision.actionId, {
result: { success: true },
durationMs: Date.now() - startTime
});

return data;
} catch (error) {
await client.logActionFailed(decision.actionId, {
error: { message: error.message }
});
throw error;
}
} else {
throw new Error(`Access denied: ${decision.reason}`);
}
}

Next Steps


Document Version: 1.0.0 | Last Updated: December 2025