Node.js SDK
| Field | Value |
|---|---|
| Document ID | ASCEND-SDK-010 |
| Version | 2026.04 |
| Last Updated | April 2026 |
| Author | Ascend Engineering Team |
| Publisher | OW-KAI Technologies Inc. |
| Classification | Enterprise Client Documentation |
| Compliance | SOC 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.
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
}
| Option | Type | Default | Description |
|---|---|---|---|
apiKey | string | Required | ASCEND API key |
agentId | string | - | Unique agent identifier |
agentName | string | - | Human-readable name |
apiUrl | string | https://pilot.owkai.app | API endpoint |
environment | string | production | Deployment environment |
failMode | FailMode | CLOSED | Behavior when unavailable |
timeout | number | 5 | Request timeout (seconds) |
maxRetries | number | 3 | Max retry attempts |
enableCircuitBreaker | boolean | true | Enable circuit breaker |
debug | boolean | false | Enable 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
| Option | Type | Required | Description |
|---|---|---|---|
actionType | string | Yes | Category.action format |
resource | string | Yes | Resource being accessed |
parameters | object | No | Action-specific parameters |
context | object | No | Execution context |
resourceId | string | No | Specific resource ID |
riskIndicators | object | No | Pre-computed risk signals |
waitForDecision | boolean | No | Wait for approval (default: true) |
timeout | number | No | Wait 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
- REST API — Direct HTTP API reference
- MCP Server Governance — MCP integration
- Python SDK — Python SDK reference
Document Version: 2026.04 | Last Updated: April 2026