TypeScript SDK

Official TypeScript SDK for TruthMark invisible watermarking with full type safety.

Installation

npm
npm install @truthmark/sdk-typescript
yarn
yarn add @truthmark/sdk-typescript

Quick Example

import { TruthMarkClient, EncodeResult, DecodeResult } from '@truthmark/sdk-typescript';

const client = new TruthMarkClient({
  apiKey: 'tm_live_your_key',
  baseUrl: 'https://truthmark-api.onrender.com'
});

// Encode watermark
const result: EncodeResult = await client.encode(
  './image.png',
  'My secret message'
);
console.log(`Download: ${result.downloadUrl}`);
console.log(`PSNR: ${result.metadata.psnr} dB`);

// Decode watermark
const decoded: DecodeResult = await client.decode('./watermarked.png');
if (decoded.found) {
  console.log(`Message: ${decoded.message}`);
  console.log(`Confidence: ${(decoded.confidence * 100).toFixed(1)}%`);
}

API Reference

new TruthMarkClient(config?)

Create a new typed client instance.

interface TruthMarkConfig {
  apiKey?: string;
  baseUrl?: string;  // defaults to production
  timeout?: number;  // request timeout in ms
}

const client = new TruthMarkClient({
  apiKey: process.env.TRUTHMARK_API_KEY!,
});

encode(imagePath, message)

Embed an invisible watermark into an image.

interface EncodeResult {
  status: string;
  metadata: {
    psnr: number;
    bits_embedded: number;
  };
  download_url: string;
  filename: string;
}

decode(imagePath)

Extract watermark from an image.

interface DecodeResult {
  found: boolean;
  message: string | null;
  confidence: number;
}

Error Handling

import { TruthMarkError } from '@truthmark/sdk-typescript';

try {
  const result = await client.encode(file, message);
} catch (error) {
  if (error instanceof TruthMarkError) {
    switch (error.status) {
      case 401: console.error('Invalid API key'); break;
      case 429: console.error(`Rate limited. Retry after ${error.retryAfter}s`); break;
      case 413: console.error('Image too large (max 20MB)'); break;
      default:  console.error(`API error: ${error.message}`);
    }
  }
}

Advanced Examples

Generic Wrapper Function

async function watermarkImage(
  imagePath: string,
  metadata: Record<string, string>
): Promise<EncodeResult> {
  const message = JSON.stringify({
    ...metadata,
    timestamp: new Date().toISOString(),
    version: '1.0',
  });
  return client.encode(imagePath, message);
}

// Usage
const result = await watermarkImage('./photo.jpg', {
  author: 'Jane Doe',
  license: 'CC-BY-4.0',
  aiModel: 'DALL-E-3',
});

Next.js Server Action

'use server';
import { TruthMarkClient } from '@truthmark/sdk-typescript';

const client = new TruthMarkClient({
  apiKey: process.env.TRUTHMARK_API_KEY!,
});

export async function watermarkAction(formData: FormData) {
  const file = formData.get('file') as File;
  const message = formData.get('message') as string;

  if (!file || !message) {
    return { error: 'Missing file or message' };
  }

  const result = await client.encode(file, message);
  return { url: result.download_url, psnr: result.metadata.psnr };
}

Batch Processing with Concurrency

import { readdir } from 'fs/promises';
import { join } from 'path';

async function batchWatermark(dir: string, message: string) {
  const files = (await readdir(dir))
    .filter(f => /\.(png|jpg|jpeg)$/i.test(f));

  const CONCURRENCY = 5;
  const results: EncodeResult[] = [];

  for (let i = 0; i < files.length; i += CONCURRENCY) {
    const batch = files.slice(i, i + CONCURRENCY);
    const batchResults = await Promise.all(
      batch.map(f => client.encode(join(dir, f), message))
    );
    results.push(...batchResults);
    console.log(`Processed ${results.length}/${files.length}`);
  }

  return results;
}

Best Practices

Use strict TypeScript config

Enable strict: true for full type safety with the SDK.

Always type your results

Use EncodeResult and DecodeResult type imports for better IDE support.

Don't expose API keys in client bundles

Use server-side routes or server actions to keep keys secure.

Need Help?

Check out the API reference or reach out to support.