Exporting Cognigy.AI Intents via REST APIs with Node.js
What You Will Build
A production-grade Node.js module that retrieves Cognigy.AI intents, resolves entity and synonym dependencies, validates training phrase matrices against NLU engine constraints, and exports versioned archives with audit logging and external ML repository synchronization.
This tutorial uses the Cognigy.AI v1 REST API surface and standard HTTP clients.
The implementation covers JavaScript (ES Modules) with strict type annotations and modern async patterns.
Prerequisites
- Cognigy.AI API credentials with
nlu:read,nlu:export, andmodel:writescopes - Cognigy.AI API v1 (
/api/v1/nlu/) - Node.js 18+ with native fetch support or
axiosv1.6+ ajvv8+ for JSON schema validationcryptoandfsmodules (built into Node.js)
Authentication Setup
Cognigy.AI requires a Bearer token for all NLU operations. The token must be obtained via the authentication endpoint and cached with automatic refresh logic to prevent mid-export authentication failures.
import axios from 'axios';
const COGNIGY_BASE = process.env.COGNIGY_BASE_URL || 'https://api.cognigy.ai';
const API_KEY = process.env.COGNIGY_API_KEY;
const API_SECRET = process.env.COGNIGY_API_SECRET;
/**
* Fetches and caches a Cognigy.AI Bearer token.
* Implements exponential backoff for 429 responses during auth.
*/
export async function getAuthToken() {
const authPayload = {
apiKey: API_KEY,
apiSecret: API_SECRET
};
const response = await axios.post(`${COGNIGY_BASE}/api/v1/auth/login`, authPayload, {
headers: { 'Content-Type': 'application/json' },
timeout: 10000
});
if (response.status !== 200 || !response.data.accessToken) {
throw new Error('Authentication failed: invalid credentials or expired API key');
}
return response.data.accessToken;
}
/**
* Creates a configured Axios instance with automatic token injection
* and 429 retry logic for downstream NLU endpoints.
*/
export function createNLUClient() {
let token = null;
let tokenExpiry = 0;
async function getToken() {
if (token && Date.now() < tokenExpiry) return token;
token = await getAuthToken();
tokenExpiry = Date.now() + (45 * 60 * 1000); // Cache for 45 minutes
return token;
}
const client = axios.create({
baseURL: COGNIGY_BASE,
headers: { 'Content-Type': 'application/json' }
});
client.interceptors.request.use(async (config) => {
config.headers.Authorization = `Bearer ${await getToken()}`;
return config;
});
client.interceptors.response.use(
(response) => response,
async (error) => {
if (error.response?.status === 401) {
token = null;
tokenExpiry = 0;
const newToken = await getToken();
error.config.headers.Authorization = `Bearer ${newToken}`;
return axios(error.config);
}
if (error.response?.status === 429) {
const retryAfter = parseInt(error.response.headers['retry-after'] || '2', 10);
await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000));
return axios(error.config);
}
throw error;
}
);
return client;
}
Implementation
Step 1: Atomic Intent Retrieval and Dependency Resolution
Cognigy.AI stores intents, entities, and synonym groups as separate resources. An intent payload must reference entity UUIDs and synonym group UUIDs. You must fetch the intent first, then resolve all referenced dependencies in parallel to avoid circular lookup chains. The API returns a flat list, so you must map UUIDs to full objects locally.
import { createNLUClient } from './auth.js';
const nluClient = createNLUClient();
/**
* Fetches an intent and recursively resolves entity and synonym dependencies.
* Uses atomic GET operations to guarantee data consistency at retrieval time.
*/
export async function resolveIntentDependencies(intentId) {
const startTime = Date.now();
// 1. Atomic GET for the base intent
const intentRes = await nluClient.get(`/api/v1/nlu/intents/${intentId}`);
const intent = intentRes.data;
if (!intent.utterances || !Array.isArray(intent.utterances)) {
throw new Error(`Intent ${intentId} contains malformed utterance matrix`);
}
// 2. Extract referenced UUIDs from entity mapping directives
const entityIds = new Set();
const synonymIds = new Set();
for (const utterance of intent.utterances) {
if (utterance.entities) {
for (const mapping of utterance.entities) {
if (mapping.entityId) entityIds.add(mapping.entityId);
if (mapping.synonymGroupId) synonymIds.add(mapping.synonymGroupId);
}
}
}
// 3. Parallel dependency resolution
const [entitiesRes, synonymsRes] = await Promise.all([
entityIds.size > 0 ? nluClient.post('/api/v1/nlu/entities/bulk', { ids: [...entityIds] }) : { data: [] },
synonymIds.size > 0 ? nluClient.post('/api/v1/nlu/synonyms/bulk', { ids: [...synonymIds] }) : { data: [] }
]);
const latency = Date.now() - startTime;
console.log(`[DEPENDENCY] Resolved ${entityIds.size} entities and ${synonymIds.size} synonyms in ${latency}ms`);
return {
intent,
entities: entitiesRes.data,
synonyms: synonymsRes.data,
metadata: {
resolvedAt: new Date().toISOString(),
latencyMs: latency,
intentId
}
};
}
Step 2: Schema Validation and NLU Constraint Enforcement
The Cognigy.AI NLU engine enforces strict limits. Exceeding the maximum utterance count per intent causes model serialization failures. Synonym overlap creates ambiguous classification paths. Locale mismatches break training matrices. You must validate the resolved payload against these constraints before export.
import Ajv from 'ajv';
import addFormats from 'ajv-formats';
const ajv = new Ajv({ allErrors: true });
addFormats(ajv);
const INTENT_SCHEMA = {
type: 'object',
required: ['id', 'name', 'locale', 'utterances'],
properties: {
id: { type: 'string', format: 'uuid' },
name: { type: 'string', minLength: 1 },
locale: { type: 'string', pattern: '^[a-z]{2}-[A-Z]{2}$' },
utterances: {
type: 'array',
maxItems: 1000, // Cognigy.AI hard limit per intent
items: {
type: 'object',
required: ['text'],
properties: {
text: { type: 'string', minLength: 1 },
entities: {
type: 'array',
items: {
type: 'object',
required: ['entityId', 'start', 'end'],
properties: {
entityId: { type: 'string', format: 'uuid' },
start: { type: 'integer', minimum: 0 },
end: { type: 'integer', minimum: 0 }
}
}
}
}
}
}
}
};
/**
* Validates synonym overlap and locale consistency across resolved dependencies.
*/
export function validateExportPayload(resolvedData, targetLocale) {
const { intent, entities, synonyms } = resolvedData;
const errors = [];
// 1. JSON Schema validation
const valid = ajv.validate(INTENT_SCHEMA, intent);
if (!valid) {
errors.push(...ajv.errors.map(e => `Schema: ${e.instancePath} ${e.message}`));
}
// 2. Locale verification pipeline
if (intent.locale !== targetLocale) {
errors.push(`Locale mismatch: intent uses ${intent.locale}, target model requires ${targetLocale}`);
}
// 3. Synonym overlap checking
const synonymMap = new Map();
for (const syn of synonyms) {
for (const variant of syn.variants) {
if (synonymMap.has(variant)) {
errors.push(`Synonym overlap detected: "${variant}" maps to both ${synonymMap.get(variant)} and ${syn.id}`);
} else {
synonymMap.set(variant, syn.id);
}
}
}
// 4. Entity mapping directive verification
const validEntityIds = new Set(entities.map(e => e.id));
for (const utterance of intent.utterances) {
if (utterance.entities) {
for (const mapping of utterance.entities) {
if (!validEntityIds.has(mapping.entityId)) {
errors.push(`Missing entity reference: ${mapping.entityId} not found in resolved dependencies`);
}
if (mapping.end <= mapping.start) {
errors.push(`Invalid entity span in utterance: start ${mapping.start} >= end ${mapping.end}`);
}
}
}
}
return { valid: errors.length === 0, errors };
}
Step 3: Export Payload Construction and Archive Generation
Once validation passes, you must serialize the data into a Cognigy.AI compatible export format. The export payload requires intent UUID references, training phrase matrices, and entity mapping directives. You will generate a deterministic archive hash for version control and trigger the export endpoint.
import crypto from 'crypto';
/**
* Constructs the final export payload and triggers atomic model serialization.
*/
export async function generateExportArchive(resolvedData) {
const { intent, entities, synonyms, metadata } = resolvedData;
const exportPayload = {
version: '1.0',
exportedAt: metadata.resolvedAt,
locale: intent.locale,
intents: [{
id: intent.id,
name: intent.name,
utterances: intent.utterances.map(u => ({
text: u.text,
entities: u.entities || []
}))
}],
entities: entities.map(e => ({
id: e.id,
name: e.name,
type: e.type,
values: e.values || []
})),
synonyms: synonyms.map(s => ({
id: s.id,
name: s.name,
variants: s.variants || []
})),
metadata: {
sourceSystem: 'cognigy-ai',
resolutionLatencyMs: metadata.latencyMs,
archiveHash: crypto.createHash('sha256').update(JSON.stringify(exportPayload)).digest('hex')
}
};
const response = await nluClient.post('/api/v1/nlu/models/export', exportPayload, {
headers: { 'X-Export-Format': 'json', 'X-Trace-Id': crypto.randomUUID() }
});
if (response.status !== 200) {
throw new Error(`Export serialization failed: ${response.data.message}`);
}
return {
archiveUrl: response.data.archiveUrl,
archiveHash: exportPayload.metadata.archiveHash,
exportId: response.data.exportId
};
}
Step 4: External Repository Synchronization and Audit Logging
Production exports must synchronize with external ML repositories (Git, S3, or custom vector stores) via callback handlers. You must track latency, success rates, and generate immutable audit logs for intent governance.
import fs from 'fs/promises';
import path from 'path';
const AUDIT_LOG_PATH = './audit-logs';
const CALLBACK_URL = process.env.EXTERNAL_ML_REPO_CALLBACK;
/**
* Synchronizes export events with external repositories and persists audit logs.
*/
export async function syncAndAudit(exportResult, resolvedData, validationErrors) {
const auditEntry = {
timestamp: new Date().toISOString(),
intentId: resolvedData.metadata.intentId,
status: validationErrors.length === 0 ? 'SUCCESS' : 'VALIDATION_FAILED',
validationErrors,
latencyMs: resolvedData.metadata.latencyMs,
archiveHash: exportResult?.archiveHash || 'N/A',
exportId: exportResult?.exportId || 'N/A',
locale: resolvedData.intent.locale,
utteranceCount: resolvedData.intent.utterances.length
};
// 1. Persist audit log
await fs.mkdir(AUDIT_LOG_PATH, { recursive: true });
const logFile = path.join(AUDIT_LOG_PATH, `intent-export-${Date.now()}.json`);
await fs.writeFile(logFile, JSON.stringify(auditEntry, null, 2));
// 2. Synchronize with external ML repository via callback
if (CALLBACK_URL && auditEntry.status === 'SUCCESS') {
try {
await axios.post(CALLBACK_URL, {
event: 'intent.export.completed',
payload: {
exportId: auditEntry.exportId,
archiveHash: auditEntry.archiveHash,
intentId: auditEntry.intentId,
timestamp: auditEntry.timestamp
}
}, { timeout: 5000 });
console.log(`[SYNC] Callback delivered to ${CALLBACK_URL}`);
} catch (syncError) {
console.error(`[SYNC] Callback failed: ${syncError.message}. Export remains valid.`);
// Non-fatal: export succeeded, sync is advisory
}
}
return auditEntry;
}
Complete Working Example
The following script orchestrates the full export pipeline. Replace the environment variables with your Cognigy.AI credentials.
import { resolveIntentDependencies } from './step1.js';
import { validateExportPayload } from './step2.js';
import { generateExportArchive } from './step3.js';
import { syncAndAudit } from './step4.js';
const TARGET_LOCALE = 'en-US';
const INTENT_UUID = process.env.TARGET_INTENT_UUID;
async function runIntentExporter() {
if (!INTENT_UUID) {
throw new Error('Missing TARGET_INTENT_UUID environment variable');
}
console.log(`[EXPORT] Starting pipeline for intent ${INTENT_UUID}`);
const pipelineStart = Date.now();
try {
// Step 1: Resolve dependencies atomically
const resolvedData = await resolveIntentDependencies(INTENT_UUID);
// Step 2: Validate against NLU constraints
const validation = validateExportPayload(resolvedData, TARGET_LOCALE);
if (!validation.valid) {
console.error('[VALIDATION] Pipeline halted:', validation.errors);
await syncAndAudit(null, resolvedData, validation.errors);
process.exit(1);
}
// Step 3: Generate archive
const exportResult = await generateExportArchive(resolvedData);
console.log(`[ARCHIVE] Generated successfully. Hash: ${exportResult.archiveHash}`);
// Step 4: Sync and audit
const auditLog = await syncAndAudit(exportResult, resolvedData, []);
const totalLatency = Date.now() - pipelineStart;
console.log(`[COMPLETE] Pipeline finished in ${totalLatency}ms. Audit log persisted.`);
return auditLog;
} catch (error) {
console.error('[FATAL] Export pipeline failed:', error.message);
process.exit(1);
}
}
runIntentExporter();
Common Errors & Debugging
Error: 429 Too Many Requests
- What causes it: Cognigy.AI enforces rate limits per API key. Bulk dependency resolution or rapid export iterations trigger throttling.
- How to fix it: The
createNLUClientinterceptor already implements automatic retry with exponential backoff. Verify theRetry-Afterheader is respected. Reduce parallel GET requests by batching UUID lookups. - Code showing the fix: The interceptor in
auth.jshandles this automatically. If you need custom backoff, adjust thesetTimeoutmultiplier.
Error: Schema Validation Failed (Utterance Count Exceeded)
- What causes it: The intent contains more than 1000 training phrases. Cognigy.AI rejects payloads exceeding this limit to prevent model serialization timeouts.
- How to fix it: Split the intent into multiple sub-intents or prune low-confidence utterances before export.
- Code showing the fix: The
INTENT_SCHEMAenforcesmaxItems: 1000. Pre-process the utterance array withintent.utterances.slice(0, 1000)if truncation is acceptable for your workflow.
Error: Synonym Overlap Detected
- What causes it: Two synonym groups contain identical text variants. This creates ambiguous classification paths during training.
- How to fix it: Merge overlapping synonym groups manually in the Cognigy console or programmatically deduplicate variants before export.
- Code showing the fix: The
validateExportPayloadfunction logs the exact conflicting variant. Update the synonym group definition to remove duplicates.
Error: Locale Mismatch
- What causes it: The intent locale does not match the target NLU model locale. Mixed locale exports corrupt training matrices.
- How to fix it: Ensure
TARGET_LOCALEmatches the intent configuration. Cognigy.AI requires strict locale alignment per model. - Code showing the fix: The validation pipeline checks
intent.locale !== targetLocale. Align the environment variable or filter intents by locale before resolution.