@ninjadoc-ai/sdk
Description
Ninjadoc AI is the first Document Q&A Platform that lets you ask questions like "What's the total?" to build extraction schemas, then use our API to get structured data with exact coordinates as proof. No training required.
This SDK provides TypeScript/JavaScript integration for document processing with a zero-boilerplate API handler, intelligent coordinate handling, and React UI overlays.
Features
- Zero-Boilerplate BFF: Use our
createApiHandler
to create a secure API proxy in your backend with a single function call. No manual endpoint creation needed. - Natural Language Q&A: Ask questions like "What's the total?" or use technical field names.
- Coordinate Precision: Get exact bounding box coordinates for every extracted field.
- React Integration: Built-in
HighlightOverlay
component for interactive document viewing. - Developer-Friendly: Clean, type-safe clients for both browser and server.
- Simple Schema Setup: Build extraction schemas by asking natural language questions, no training required.
Installation
npm install @ninjadoc-ai/sdk
Framework Adapters: Zero-Friction Integration
The SDK now includes framework-specific adapters that reduce integration friction to almost zero. These adapters handle framework-specific patterns, environment variables, and error handling automatically.
Remix Adapter
For Remix applications, use the framework adapter for seamless integration:
// app/routes/api.ninjadoc.$.tsx
import { createRemixApiRoute } from '@ninjadoc-ai/sdk/frameworks/remix';
// Zero-friction setup - API key automatically read from NINJADOC_API_KEY
const { loader, action } = createRemixApiRoute();
export { loader, action };
That's it! The adapter automatically:
- Reads your API key from environment variables
- Handles Remix's loader/action pattern
- Provides framework-appropriate error responses
- Manages request/response conversion
Zero-Friction Browser Client
The browser client now automatically detects your route setup:
// No configuration needed - works out of the box!
const client = createBrowserClient();
// The client automatically:
// 1. Tries framework adapter routes (/api/ninjadoc/*)
// 2. Falls back to legacy routes (/api/extract, /api/jobs/*/status)
// 3. Remembers the working pattern for future requests
This means developers can just call createBrowserClient()
with no parameters and it will work with both new framework adapters and existing legacy setups!
Next.js Adapter
For Next.js App Router applications:
// app/api/ninjadoc/[...path]/route.ts
import { createNextApiRoute } from '@ninjadoc-ai/sdk/frameworks/next';
// Zero-friction setup - API key automatically read from NINJADOC_API_KEY
const handler = createNextApiRoute();
export { handler as GET, handler as POST, handler as PUT, handler as DELETE };
The Next.js adapter automatically:
- Reads your API key from environment variables
- Handles NextRequest/NextResponse objects
- Provides framework-appropriate error responses
- Manages dynamic route parameters
Advanced Configuration
Both adapters support advanced configuration:
const { loader, action } = createRemixApiRoute({
apiKey: 'your-explicit-api-key', // or omit to use env var
apiKeyEnv: 'CUSTOM_ENV_VAR', // default: 'NINJADOC_API_KEY'
onError: (error, context) => {
// Custom error handling
return new Response(JSON.stringify({ error: error.message }), {
status: 500
});
}
});
Quick Start: Zero-Friction Integration
The easiest way to get started is using our framework adapters that handle everything automatically.
1. Add Framework Adapter Route
For Remix:
// app/routes.ts - Add this line:
route("/api/ninjadoc/*", "routes/api.ninjadoc.$.tsx"),
// app/routes/api.ninjadoc.$.tsx
import { createRemixApiRoute } from '@ninjadoc-ai/sdk/frameworks/remix';
const { loader, action } = createRemixApiRoute();
export { loader, action };
For Next.js:
// app/api/ninjadoc/[...path]/route.ts
import { createNextApiRoute } from '@ninjadoc-ai/sdk/frameworks/next';
const handler = createNextApiRoute();
export { handler as GET, handler as POST, handler as PUT, handler as DELETE };
2. Use the Client (Zero Configuration)
// Your React Component
import { createBrowserClient } from '@ninjadoc-ai/sdk';
import { useState } from 'react';
function MyUploader() {
const [jobId, setJobId] = useState<string | null>(null);
const [document, setDocument] = useState<any | null>(null);
const handleFileChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (!file) return;
// Zero configuration - automatically detects your route setup
const client = createBrowserClient();
// 1. Extract document via your app's API handler
const job = await client.extractDocument(file, 'your-processor-uuid');
setJobId(job.id);
// 2. Wait for completion and get the processed document
const processedDoc = await client.processDocument(job.id);
setDocument(processedDoc);
// 3. Access data in multiple ways:
// Find specific regions for visualization
const totalRegion = client.findRegion(processedDoc.regions, 'invoice_total');
console.log('Total:', totalRegion?.value);
console.log('AI Answer:', totalRegion?.ai_answer);
// Access raw field data with AI answers and confidence
processedDoc.regions.forEach(region => {
console.log(`Field: ${region.label}`);
console.log(`AI Answer: ${region.ai_answer}`);
console.log(`Confidence: ${region.confidence}`);
console.log(`Coordinates:`, region.geometry);
});
};
return <input type="file" onChange={handleFileChange} />;
}
That's it! The framework adapter automatically:
- Reads your
NINJADOC_API_KEY
environment variable - Handles framework-specific request/response patterns
- Provides appropriate error handling
- The browser client auto-detects your route setup
Manual Setup (Legacy)
If you prefer manual control, you can still use the original approach:
React Integration
Use the <HighlightOverlay />
component to visualize the extracted data on top of your document viewer.
import { createBrowserClient } from '@ninjadoc-ai/sdk';
import { HighlightOverlay } from '@ninjadoc-ai/sdk/react';
import { useState, useEffect } from 'react';
import type { ProcessedDocument } from '@ninjadoc-ai/sdk';
function DocumentViewer({ jobId }: { jobId: string }) {
const [document, setDocument] = useState<ProcessedDocument | null>(null);
// Zero configuration - automatically detects your route setup
const client = createBrowserClient();
useEffect(() => {
// The processDocument method polls for job completion and returns
// the final document with regions, page dimensions, and raw field data.
client.processDocument(jobId).then(setDocument);
}, [jobId]);
if (!document) return <div>Loading...</div>;
return (
<div style={{ position: 'relative' }}>
{/* Your PDF viewer here */}
<HighlightOverlay
document={document}
containerSize={{ width: 800, height: 600 }}
onRegionClick={(region) => {
console.log(`Clicked ${region.label}:`, region.value);
console.log(`AI Answer: ${region.ai_answer}`);
}}
/>
{/* Access all field data */}
{document.regions.map((region, index) => (
<div key={index}>
<strong>{region.label}:</strong> {region.ai_answer}
(Confidence: {region.confidence}%)
</div>
))}
</div>
);
}
API Reference
Browser Client (@ninjadoc-ai/sdk
)
The browser client is designed to talk to your application's backend (the API handler), not directly to the Ninjadoc AI API. It now features automatic route detection for zero-configuration setup.
createBrowserClient(options)
const client = createBrowserClient({
baseUrl?: string; // Optional: auto-detects if not provided
credentials?: RequestCredentials; // default: 'include'
autoDetectRoutes?: boolean; // default: true - automatically detects route patterns
});
Zero-Configuration Usage:
// Automatically detects route patterns with zero configuration
const client = createBrowserClient();
// Automatically detects:
// - Framework adapter routes (/api/ninjadoc/*)
// - Legacy routes (/api/extract, /api/jobs/*/status)
// - Remembers working pattern for performance
client.extractDocument(file, processorId)
Creates a new extraction job by POSTing to your /api/ninjadoc/extract
endpoint.
client.getJobStatus(jobId)
Gets the status of a job by GETting from your /api/ninjadoc/jobs/{jobId}/status
endpoint.
client.processDocument(jobId)
A convenience method that polls getJobStatus
until the job is complete, then returns the fully processed document, including regions with all field data and page dimensions.
Returns a ProcessedDocument
object with:
jobId
: The job identifierstatus
: Complete job status informationregions
: All extracted fields with AI answers, confidence scores, coordinates, and referencespageDimensions
: Page size information for coordinate scaling
client.getRegions(jobStatus)
Transforms a completed job status object into an array of Region
objects.
client.findRegion(regions, label)
Finds a specific region by its label from an array of regions.
Server Handler (@ninjadoc-ai/sdk/server
)
The server package contains tools for your backend.
createApiHandler(options)
Creates a framework-agnostic request handler that securely proxies calls to the Ninjadoc AI API.
import { createApiHandler } from '@ninjadoc-ai/sdk/server';
const handler = createApiHandler({
apiKey: string; // Your secret Ninjadoc AI API key
});
// Usage:
// await handler(request, subpath);
createServerClient(options)
If you need more control and want to build your own backend logic without the pre-built handler, you can use the createServerClient
directly.
import { createServerClient } from '@ninjadoc-ai/sdk/server';
const serverClient = createServerClient({
apiKey: string; // Your secret Ninjadoc AI API key
});
// Now you can call methods like serverClient.extractDocument(...)
HighlightOverlay Component
The props for the React component remain the same.
interface HighlightOverlayProps {
document: ProcessedDocument;
containerSize: { width: number; height: number };
onRegionClick?: (region: Region) => void;
activeRegionId?: string;
}
Framework Adapters API Reference
Remix Adapter
createRemixApiRoute(options?)
Creates a complete Remix API route with loader and action functions.
import { createRemixApiRoute } from '@ninjadoc-ai/sdk/frameworks/remix';
const { loader, action } = createRemixApiRoute({
apiKey?: string; // Optional: API key (auto-read from env if omitted)
apiKeyEnv?: string; // Optional: Env var name (default: 'NINJADOC_API_KEY')
onError?: (error, context) => Response; // Optional: Custom error handler
});
createRemixHandler(options?)
Creates individual loader and action handlers with more control.
import { createRemixHandler } from '@ninjadoc-ai/sdk/frameworks/remix';
const { loader, action } = createRemixHandler(options);
Next.js Adapter
createNextApiRoute(options?)
Creates a Next.js API route handler for all HTTP methods.
import { createNextApiRoute } from '@ninjadoc-ai/sdk/frameworks/next';
const handler = createNextApiRoute({
apiKey?: string; // Optional: API key (auto-read from env if omitted)
apiKeyEnv?: string; // Optional: Env var name (default: 'NINJADOC_API_KEY')
onError?: (error, context) => NextResponse; // Optional: Custom error handler
});
export { handler as GET, handler as POST, handler as PUT, handler as DELETE };
createNextHandler(options?)
Creates a Next.js handler with more control.
import { createNextHandler } from '@ninjadoc-ai/sdk/frameworks/next';
const handler = createNextHandler(options);