Docs/Node.js Guide
Node.js / JavaScript

Node.js Integration Guide

Govern AI content in Node.js — either on-device with tork-governance, or server-side via the REST API. Covers fetch, axios, middleware, and Express.

Prerequisites

  • Node.js 18+ (for native fetch) or Node.js 14+ with axios
  • No API key is needed for on-device governance. A Tork API key is required only for the REST examples (get one here)
  • Basic knowledge of async/await (for the REST path only)

Two different packages — pick deliberately

tork-governance (npm) is the on-device package. It exports Tork, and govern() is synchronous — do not await it. tork-governance makes the governance decision on your machine. PII detection, redaction and the returned verdict are computed on-device. With no API key it makes zero network calls: prompts, completions and detected PII values never leave the machine, and nothing appears in your dashboard.

@torknetwork/sdk (npm, v2.0.0) is the cloud package. It exports TorkClient, sends content to Tork, and Tork decides server-side and writes a receipt to your dashboard. The REST examples on this page are that same server-governed path without an SDK.

TorkClient is not exported by tork-governance and never has been — it is undefined if you import it from there.

Local governance is free and unlimited — decisions made on your own machine are never metered. Evidence is what is metered: attestations, receipts and anchoring.

Installation

Pick the package that matches where you want the decision made.

bash
# ON-DEVICE — the decision is made in your process.
# Exports the Tork class. govern() is synchronous.
npm install tork-governance

# CLOUD — Tork makes the decision server-side and writes a
# receipt to your dashboard. Exports TorkClient.
npm install @torknetwork/sdk

# The REST examples below need no SDK at all (fetch is built in).
npm install axios  # optional, only for the axios example

On-device SDK (tork-governance)

Decisions are made in your process. No API key, no network call.

javascript
import { Tork } from 'tork-governance';

// No API key: the decision is made in this process and nothing
// is sent anywhere. This is free and unlimited.
const tork = new Tork();

// govern() is synchronous — there is no network call to await.
const result = tork.govern('Check this text for PII: john@example.com');

console.log(result.action);            // "redact"
console.log(result.output);            // "Check this text for PII: [EMAIL_REDACTED]"
console.log(result.receipt.receiptId); // "rcpt_..." — a LOCAL receipt, not in your dashboard

// Fields: action, output, pii, receipt, report.
// There is no "redacted" field — the governed text is "output".
if (result.pii.hasPII) {
  console.log(result.pii.types);  // ["email"]
  console.log(result.pii.count);  // 1
}

Supply an API key and the SDK additionally reports a metadata-only attestation of each decision — the action taken, PII type labels and counts, a risk classification, policy labels and a salted fingerprint. It never sends input text, output text or PII values. The decision itself is still made on-device and is never delayed or changed by reporting. Those attestations appear in your dashboard and are included in the daily on-chain anchor — each one recorded as a client attestation (capture_mode=edge, attested_by=client), a claim Tork recorded but did not execute and cannot independently verify. Requires tork-governance 0.24.0+ (PyPI) or 0.11.0+ (npm).

A decision reported by an on-device SDK is recorded as a client attestation (capture_mode=edge, attested_by=client): a claim Tork recorded but did not execute and cannot independently verify. A decision made by @torknetwork/sdk is recorded as capture_mode=cloud, attested_by=tork — Tork made that call itself. Both are equally immutable once anchored; they differ in what is immutable. A client attestation freezes your claim. A server-governed receipt freezes Tork's own decision.

Environment Setup

For the REST examples below. The on-device SDK does not use an API key.

bash.env
# .env
# Used by the REST examples below. The on-device package does not
# require an API key, and the published builds never send one.
TORK_API_KEY=tork_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx

Never commit your API key to version control. Add .env to your .gitignore.

Server-governed REST API

Send content to Tork and get a dashboard receipt back. Requires an API key.

javascript
// Using native fetch (Node.js 18+)
// This SENDS the content to Tork, which decides server-side.
const response = await fetch('https://tork.network/api/v1/govern', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${process.env.TORK_API_KEY}`
  },
  body: JSON.stringify({
    content: userMessage,
    options: { mode: 'redact' }
  })
});

const result = await response.json();

console.log(result.action);      // "allow" | "redact" | "deny"
console.log(result.output);      // the governed (redacted) text
console.log(result.receipt_id);  // "rcpt_..." — appears in your dashboard

if (result.action !== 'allow') {
  console.log('PII detected:', result.pii_detected);
}

Reusable Client

Create a wrapper function for cleaner code across your application.

javascripttork.js
// tork.js - Reusable REST wrapper (server-governed path)
const TORK_API_URL = 'https://tork.network/api/v1';

export async function governContent(content, options = {}) {
  const { mode = 'redact' } = options;

  const response = await fetch(`${TORK_API_URL}/govern`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${process.env.TORK_API_KEY}`
    },
    body: JSON.stringify({ content, options: { mode } })
  });

  if (!response.ok) {
    throw new Error(`Tork API error: ${response.status}`);
  }

  return response.json();
}

// Usage — returns { action, output, receipt_id, ... }
const result = await governContent(userInput);

Error Handling

Handle API errors, rate limits, and network issues gracefully.

javascript
async function safeGovern(content) {
  try {
    const response = await fetch('https://tork.network/api/v1/govern', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${process.env.TORK_API_KEY}`
      },
      body: JSON.stringify({ content, options: { mode: 'redact' } })
    });

    // Handle HTTP errors
    if (!response.ok) {
      if (response.status === 401) {
        throw new Error('Invalid API key');
      }
      if (response.status === 429) {
        throw new Error('Rate limit exceeded. Please retry later.');
      }
      throw new Error(`API error: ${response.status}`);
    }

    return await response.json();

  } catch (error) {
    // Network errors, timeouts, etc.
    if (error.name === 'AbortError') {
      throw new Error('Request timed out');
    }
    throw error;
  }
}

// With timeout
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000);

try {
  const response = await fetch(url, { signal: controller.signal });
  // ...
} finally {
  clearTimeout(timeout);
}

Common Error Codes

401Invalid or missing API key
429Rate limit exceeded (limit depends on your plan)
400Invalid request body
500Server error (retry with backoff)

Express Middleware

Protect your API endpoints with a reusable middleware.

javascriptmiddleware/torkGuard.js
// middleware/torkGuard.js
import { Tork } from 'tork-governance';

// On-device: construct once and share. No client to close, and no
// network call in the request path.
const tork = new Tork();

export function torkGuard(options = {}) {
  const { blockDenied = true, logOnly = false } = options;

  return (req, res, next) => {
    // Skip if there is no body to govern
    if (!req.body?.message && !req.body?.content) {
      return next();
    }

    const content = req.body.message || req.body.content;

    try {
      // Synchronous — no await.
      const result = tork.govern(content);

      // Attach the governed result for downstream handlers
      req.torkResult = result;

      if (result.action !== 'allow') {
        console.warn('Governance action taken:', {
          action: result.action,
          piiTypes: result.pii.types,
          receiptId: result.receipt.receiptId
        });

        if (result.action === 'deny' && blockDenied && !logOnly) {
          return res.status(400).json({
            error: 'Content blocked by policy',
            receiptId: result.receipt.receiptId
          });
        }
      }

      next();

    } catch (error) {
      // Your own error-handling policy: decide whether a failure in
      // YOUR governance call lets the request through or blocks it.
      console.error('Governance call threw:', error);
      if (options.failClosed) {
        return res.status(503).json({ error: 'Governance check failed' });
      }
      next();
    }
  };
}

Complete Express Example

A full example showing Tork integration in an Express chat API.

javascriptserver.js
import express from 'express';
import { Tork } from 'tork-governance';
import { torkGuard } from './middleware/torkGuard.js';

const tork = new Tork();
const app = express();
app.use(express.json());

// Apply Tork guard to all AI-related routes
app.use('/api/chat', torkGuard({ context: 'chatbot' }));
app.use('/api/generate', torkGuard({ context: 'content-generation' }));

// Chat endpoint with Tork protection
app.post('/api/chat', async (req, res) => {
  const { conversationId } = req.body;

  // Content already governed by middleware.
  // req.torkResult holds the full GovernanceResult.
  const governed = req.torkResult;

  try {
    // Send the REDACTED text to your model, not the raw message.
    const aiResponse = await generateAIResponse(governed.output, conversationId);

    // Govern the model's output on the way back out too.
    const outputResult = tork.govern(aiResponse);

    res.json({
      response: outputResult.output,
      filtered: outputResult.action !== 'allow'
    });

  } catch (error) {
    res.status(500).json({ error: 'Failed to generate response' });
  }
});

app.listen(3000, () => {
  console.log('Server running with Tork protection');
});

TypeScript Support

Type definitions for better IDE support and type safety.

typescripttypes/tork.ts
// The on-device package ships its own types — import them directly.
import {
  Tork,
  type GovernanceResult,
  type GovernanceAction,
  type PIIDetectionResult,
} from 'tork-governance';

const tork = new Tork();

// govern() is synchronous: GovernanceResult, not Promise<GovernanceResult>.
const result: GovernanceResult = tork.govern('john@example.com');

const action: GovernanceAction = result.action;   // 'allow'|'deny'|'redact'|'escalate'
const pii: PIIDetectionResult = result.pii;       // { hasPII, types, count, matches, redactedText }
const receiptId: string = result.receipt.receiptId;

// --- REST path (server-governed) types you write yourself ---
interface TorkGovernRequest {
  content: string;
  options?: { mode?: 'detect' | 'redact' | 'deny' };
}

interface TorkGovernResponse {
  action: 'allow' | 'redact' | 'deny';
  output: string;
  receipt_id: string;
  latency_ms: number;
  pii_detected?: Array<{ type: string; count: number }>;
}

async function governContent(
  request: TorkGovernRequest
): Promise<TorkGovernResponse> {
  const response = await fetch('https://tork.network/api/v1/govern', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${process.env.TORK_API_KEY}`
    },
    body: JSON.stringify(request)
  });

  if (!response.ok) {
    throw new Error(`Tork API error: ${response.status}`);
  }

  return response.json();
}

Batch Processing

Evaluate multiple items while respecting rate limits.

javascript
import { Tork } from 'tork-governance';

// ON-DEVICE: no network, no rate limit, nothing to batch around.
// A plain synchronous loop is the fastest correct answer.
const tork = new Tork();

const messages = [
  'Hello, how are you?',
  'My email is john@example.com',
  'Card 4111 1111 1111 1111',
];

const results = messages.map(content => tork.govern(content));
const flagged = results.filter(r => r.action !== 'allow');

console.log(`${flagged.length} of ${results.length} were redacted or denied`);

// ---------------------------------------------------------------
// CLOUD/REST: here batching and pacing DO matter, because each
// item is a metered HTTP round-trip subject to rate limits.
async function governBatchViaRest(items, { concurrency = 5, delayMs = 100 } = {}) {
  const out = [];

  for (let i = 0; i < items.length; i += concurrency) {
    const batch = items.slice(i, i + concurrency);

    const batchResults = await Promise.all(
      batch.map(item =>
        governContent(item.content).catch(err => ({ error: err.message, item }))
      )
    );

    out.push(...batchResults);

    if (i + concurrency < items.length) {
      await new Promise(r => setTimeout(r, delayMs));
    }
  }

  return out;
}

Best Practices

Know which package you installed

tork-governance exports Tork and decides on-device. TorkClient belongs to @torknetwork/sdk and decides server-side. Mixing them is the single most common integration failure.

Don't await the on-device govern()

It returns a GovernanceResult, not a Promise. Awaiting it still works in JS but signals the wrong mental model — there is no round-trip and no client to close.

Govern both input and output

Govern user messages before sending them to your model, and model responses before showing them to users. Pass result.output downstream, not the original text.

Implement retry logic on the REST path only

Use exponential backoff for 429 and 5xx. Don't retry 401 or 400. The on-device path has no HTTP errors to retry.

Log receipt IDs

Store receipt.receiptId (on-device) or receipt_id (REST) for debugging and compliance.

Decide how YOUR call fails

Wrap your own governance call and choose whether an unexpected error in your code path allows or blocks the request. This is your error-handling policy, not a statement about Tork's availability.

Next Steps

See the API reference for the full /api/v1/govern contract, or the quickstart for the shortest path from install to a first decision.

Documentation

Learn to integrate TORK

Upgrade Plan

Current: free

Support

Get help from our team