Skip to main content

Node.js SDK

FieldValue
Document IDASCEND-SDK-010
Version2026.04
Last UpdatedApril 2026
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-ai/sdk) provides enterprise-grade governance integration for TypeScript and JavaScript AI agents. It includes full TypeScript types, circuit breaker patterns, and fail mode configuration.

API Key Security

Store API keys in environment variables, never in source code or version control. Leaked keys grant full governance access to your organization's agents and actions.

Installation

npm install @ascend-ai/sdk@2.1.0
# or
yarn add @ascend-ai/sdk@2.1.0

Requirements

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

Verify Installation

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

Quick Start

// Source: sdk/nodejs/src/index.ts:22
import { AscendClient, FailMode, Decision } from '@ascend-ai/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-ai/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-ai/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-ai/sdk';

Production Error Handling

import {
AscendClient,
AuthenticationError,
ConnectionError,
CircuitBreakerOpenError,
TimeoutError
} from '@ascend-ai/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-ai/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-ai/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-ai/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}`);
}
}

Troubleshooting

AuthenticationError when creating AscendClient

Cause: The API key is not set or the ASCEND_API_KEY environment variable is empty. The SDK validates credentials during construction. Solution: Set the environment variable: export ASCEND_API_KEY=ask_your_key_here. Or pass directly: new AscendClient({ apiKey: 'ask_...' }). Verify the key is valid by testing: curl -H "X-API-Key: $ASCEND_API_KEY" https://pilot.owkai.app/health.

CircuitBreakerOpenError after multiple failures

Cause: The circuit breaker has opened after consecutive failures to reach the ASCEND API. While open, all requests are immediately rejected without making API calls. Solution: The circuit breaker will automatically attempt recovery after the configured timeout (default 30 seconds). Check network connectivity to pilot.owkai.app. If the API is healthy but the circuit breaker persists, restart your application to reset the breaker state.

TypeScript type errors with response objects

Cause: The SDK types may not match the API response if you are using an older SDK version or the API has been updated. Solution: Update to the latest SDK version: npm install @ascend-ai/sdk@2.1.0. If using TypeScript, ensure your tsconfig.json has "strict": true and "esModuleInterop": true.

Next Steps


Document Version: 2026.04 | Last Updated: April 2026