TypeScript SDK for the Witnium Vault API
npm install @witniumtech/vault-sdkA TypeScript SDK for interacting with the Witnium Vault API. Provides type-safe access to all API endpoints with cookie-based authentication for seamless browser integration.
First, configure npm to use GitHub Packages for the @witnium scope. Add to your .npmrc:
```
@witnium:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=YOUR_GITHUB_TOKEN
Then install:
`bash`
npm install @witnium/vault-sdk
`typescript
import { WitniumVaultClient } from '@witnium/vault-sdk';
// Create a client
const client = new WitniumVaultClient({
baseUrl: 'https://witniumvault.witnium.tech'
});
// Login (sets session cookie)
await client.login({
email: 'user@example.com',
password: 'your-password'
});
// List files
const result = await client.files.list({ page: 1, limit: 20 });
if (result.success) {
console.log('Files:', result.data.files);
}
`
- Type-safe: Full TypeScript support with generated types from OpenAPI spec
- Cookie-based auth: Seamless browser integration with automatic session management
- Generic + Convenience methods: Use client.get('/api/files') or client.files.list()
- Geospatial search: Find files within a radius of any location (GPS coordinates)
- Visual similarity search: Find images similar to another image, text description, or uploaded photo using CLIP AI
- Self-documenting: Runtime endpoint registry for AI tools (Lovable, etc.)
- Error handling: Structured error responses with typed error objects
The SDK uses cookie-based authentication, which works automatically in browser environments:
`typescript
// Login
await client.login({
email: 'user@example.com',
password: 'password'
});
// Check authentication status
const status = await client.getAuthStatus();
if (status.authenticated) {
console.log('Logged in as:', status.user?.email);
}
// Logout
await client.logout();
`
If the user has MFA enabled, provide the TOTP code:
`typescript
await client.login({
email: 'user@example.com',
password: 'password',
totp: '123456' // From authenticator app
});
// Or use a backup code
await client.login({
email: 'user@example.com',
password: 'password',
backupCode: 'ABCD-EFGH-1234'
});
`
The SDK provides generic HTTP methods that work with any endpoint:
`typescript
// GET request
const result = await client.get('/api/files', { page: '1', limit: '20' });
// POST request (search)
const result = await client.post('/api/search', {
query: 'contract',
path: '/documents'
});
// PUT request
const result = await client.put('/api/files', {
action: 'restore',
fileId: 'file-123'
});
// DELETE request
const result = await client.delete('/api/files', { id: 'file-123' });
`
For common operations, use the typed convenience methods:
#### Files
`typescript
// List files with pagination
const files = await client.files.list({ page: 1, limit: 20 });
// Get a specific file
const file = await client.files.get('file-123');
// Delete a file (soft delete)
await client.files.delete('file-123');
// Permanently delete
await client.files.delete('file-123', true);
// Restore a deleted file
await client.files.restore('file-123');
// Get version history
const history = await client.files.getHistory('file-group-123');
`
#### Search
`typescript
// Simple search
const results = await client.search.query('contract');
// Search with filters
const results = await client.search.query('invoice', {
fileTypes: ['pdf', 'docx'],
dateRange: { from: '2024-01-01' },
folderPath: '/documents',
limit: 20
});
// Geospatial search - find files within radius of a location
const results = await client.search.query('photo', {
location: {
latitude: 59.3293, // Center point latitude
longitude: 18.0686, // Center point longitude
radiusKm: 10 // Search radius in kilometers
}
});
// Combine geospatial with other filters
const results = await client.search.query('', {
fileTypes: ['image/'],
location: {
latitude: 40.7128,
longitude: -74.0060,
radiusKm: 5
},
limit: 50
});
// Visual similarity search - find images similar to another image
const similar = await client.search.findSimilar({ fileId: 'image-123' });
if (similar.success) {
console.log('Similar images:', similar.data.results);
similar.data.results.forEach(img => {
console.log(${img.filename}: ${(img.similarity * 100).toFixed(1)}% similar);
});
}
// Visual similarity search - find images matching a text description
const textResults = await client.search.findSimilar({ text: 'red sports car' });
// Visual similarity search - upload an image to find similar ones
const imageData = btoa(/ raw image bytes /); // Base64 encoded
const uploadResults = await client.search.findSimilar({
imageData,
imageMimeType: 'image/jpeg'
});
`
#### Evidence Binders
`typescript
// List binders
const binders = await client.evidenceBinders.list();
// Create a binder
const binder = await client.evidenceBinders.create({
name: 'Q1 2024 Contracts',
description: 'All contracts signed in Q1 2024'
});
// Export a binder
const exportJob = await client.evidenceBinders.export('binder-123', 'pdf');
`
#### Workspace
`typescript
// Get audit logs
const logs = await client.workspace.getAuditLogs({
page: 1,
limit: 50,
eventType: 'file.upload'
});
// Get folder tree
const tree = await client.workspace.getFolderTree();
// Get usage statistics
const usage = await client.workspace.getUsage();
`
#### Workspaces (Switching Between Workspaces)
Users can have both a personal workspace and access to multiple organization workspaces. The SDK makes it easy to switch between them:
`typescriptPending invitation: ${ws.name}
// List all available workspaces
const result = await client.workspaces.list();
if (result.success) {
for (const ws of result.data.clients) {
if (ws.isPending) {
console.log();${ws.name} (${ws.role})
} else {
console.log();
}
}
}
// Switch to a specific workspace
const switchResult = await client.workspaces.switchTo('org-123');
if (switchResult.success) {
console.log(Switched to ${switchResult.data.clientName});Role: ${switchResult.data.role}
console.log();Workspace ID: ${switchResult.data.workspaceId}
console.log();
// All subsequent API calls now use this workspace
const files = await client.files.list();
}
// Switch to personal workspace
await client.workspaces.switchToPersonal();
// Get current workspace context
const current = await client.workspaces.getCurrent();
if (current.success && current.data.authenticated) {
console.log(Current workspace: ${current.data.workspaceId});Is personal: ${current.data.isPersonalWorkspace}
console.log();`
}
How it works: When you switch workspaces, the SDK calls an endpoint that regenerates your session token with the new workspace context. The browser automatically stores the updated session cookie, even for cross-origin requests. All subsequent API calls will operate in the context of the new workspace.
#### Statistics
`typescript
// Get comprehensive file statistics
const stats = await client.statistics.getFileStats();
if (stats.success) {
console.log('Total files:', stats.data.overview?.totalFiles);
console.log('Total storage:', stats.data.overview?.totalStorageFormatted);
}
// Get specific sections with options
const stats = await client.statistics.getFileStats({
startDate: '2024-01-01',
endDate: '2024-12-31',
granularity: 'month',
sections: ['overview', 'timeline', 'typeDistribution'],
topN: 5
});
// Convenience methods for common stats
const overview = await client.statistics.getOverview();
const timeline = await client.statistics.getTimeline('month');
const types = await client.statistics.getTypeDistribution(10);
const users = await client.statistics.getUserActivity(10);
`
Available sections: overview, timeline, typeDistribution, folderDistribution, userActivity, processing, versioning, witness, externalSources, deletions
#### Digital Witnesses
`typescript
// Get blockchain info for a witness
const info = await client.witness.getBlockchainInfo('witness-id-hex');
if (info.success) {
console.log('Confirmations:', info.data.data.confirmations);
console.log('Block number:', info.data.data.blockNumber);
console.log('Witnessed data:', info.data.data.witnessedData);
// Quadruple-layer protection data
console.log('Data signature:', info.data.data.signature);
console.log('Witness nonce:', info.data.data.witnessNonce);
console.log('Witness signature R:', info.data.data.witnessSignatureR);
console.log('Witness signature S:', info.data.data.witnessSignatureS);
console.log('Original timestamp:', info.data.data.originalTimestamp);
console.log('Signing public key:', info.data.data.signingPublicKey);
}
`
#### Account (Personal Settings)
`typescriptTrial: ${status.data.daysRemaining} days left
// Get subscription status
const status = await client.account.getSubscriptionStatus();
if (status.success) {
if (status.data.isInTrial) {
console.log();
}
}
// Create checkout session for subscription
const checkout = await client.account.createCheckoutSession({
tier: 'pro',
interval: 'yearly'
});
if (checkout.success) {
window.location.href = checkout.data.url;
}
// Get billing portal URL
const portal = await client.account.getBillingPortalUrl();
// MFA Management
const mfaStatus = await client.account.getMfaStatus();
const setup = await client.account.startMfaSetup();
await client.account.confirmMfaSetup(setupToken, '123456');
await client.account.disableMfa({ totpCode: '123456' });
const codes = await client.account.regenerateBackupCodes();
// Passkey Management
const options = await client.account.startPasskeyRegistration();
await client.account.finishPasskeyRegistration({ credential, friendlyName: 'MacBook' });
await client.account.updatePasskey(credentialId, { friendlyName: 'New Name' });
await client.account.removePasskey(credentialId);
// Profile Management
const profile = await client.account.getProfile();
await client.account.updateProfile({ name: 'New Name' });
// AI Settings (personal workspace)
const aiSettings = await client.account.getAISettings();
await client.account.updateAISettings({ enabled: true });
`
#### Admin (Organization Management)
`typescript
import { Role } from '@witnium/vault-sdk';
// User Management
const users = await client.admin.listUsers();
const user = await client.admin.getUser('user-123');
await client.admin.updateUserRole('user-123', Role.MANAGER);
await client.admin.toggleUserStatus('user-123', false); // disable user
await client.admin.removeUser('user-123');
// Invitations
const invitations = await client.admin.listInvitations();
await client.admin.inviteUser('new@example.com', Role.USER);
await client.admin.resendInvitation('invitation-123');
await client.admin.cancelInvitation('invitation-123');
// Organization AI Settings
const orgAi = await client.admin.getAISettings();
await client.admin.updateAISettings({
enabled: true,
chatModel: 'claude-3-opus',
excludedPaths: ['/sensitive/**']
});
// Subscription
const subscription = await client.admin.getSubscription();
const checkoutUrl = await client.admin.createCheckoutSession('team', 'year');
const portalUrl = await client.admin.getPortalUrl();
// Audit Logs
const logs = await client.admin.getAuditLogs({
limit: 50,
action: 'file.upload',
startDate: '2024-01-01T00:00:00Z'
});
`
All methods return an ApiResult type that indicates success or failure:
`typescript
const result = await client.files.get('invalid-id');
if (result.success) {
console.log('File:', result.data.file);
} else {
console.error('Error:', result.error.error);
console.error('Status:', result.error.status);
console.error('Code:', result.error.code);
}
`
For authentication errors, you can use the WitniumApiError class:
`typescript
import { WitniumApiError } from '@witnium/vault-sdk';
try {
await client.login({ email: 'user@example.com', password: 'wrong' });
} catch (error) {
if (error instanceof WitniumApiError) {
if (error.isUnauthorized()) {
console.log('Invalid credentials');
}
if (error.code === 'MFA_REQUIRED') {
console.log('MFA code needed');
}
}
}
`
The SDK includes a runtime endpoint registry for AI tools like Lovable:
`typescript
import { EndpointRegistry } from '@witnium/vault-sdk';
// Get info about an endpoint
const info = EndpointRegistry.getEndpoint('/api/files');
console.log(info?.methods.GET?.description);
// Output: "List files in the current workspace"
// List all endpoints
const endpoints = EndpointRegistry.listEndpoints();
// Find endpoints by tag
const authEndpoints = EndpointRegistry.findByTag('Auth');
// Search endpoints
const fileEndpoints = EndpointRegistry.search('upload');
// Get a summary for AI context
const summary = EndpointRegistry.getSummary();
console.log(summary);
// Output: Markdown-formatted list of all endpoints
`
`typescript`
const client = new WitniumVaultClient({
// Required: API base URL
baseUrl: 'https://witniumvault.witnium.tech',
// Optional: Custom timeout (default: 30000ms)
timeout: 60000,
// Optional: Default headers for all requests
defaultHeaders: {
'X-Custom-Header': 'value'
},
// Optional: Custom fetch implementation (for testing)
fetch: customFetchFunction
});
The SDK exports all types for use in your application:
`typescript
import type {
// Client types
WitniumVaultClientConfig,
ApiResult,
ApiError,
LoginCredentials,
// API types
FileMetadata,
FilesListResponse,
EvidenceBinder,
SearchResult,
// Resource options
ListFilesOptions,
SearchOptions,
CreateBinderOptions,
// Statistics types
FileStatsOptions,
FileStatsResponse,
OverviewStats,
TimelineStats,
TypeDistributionStats,
} from '@witnium/vault-sdk';
`
`typescript
// File uploads use multipart/form-data
// Location is optional and becomes part of the immutable Digital Witness
const formData = new FormData();
formData.append('file', fileObject);
formData.append('folderPath', '/photos/vacation');
// Add location (GeoJSON Point format) - this is immutable once set
// The location becomes part of the blockchain-verified Digital Witness
formData.append('location', JSON.stringify({
type: 'Point',
coordinates: [18.0686, 59.3293] // [longitude, latitude]
}));
// Optional: add custom metadata (also included in Digital Witness)
formData.append('customMetadata', JSON.stringify({
camera: 'iPhone 15 Pro',
description: 'Beach sunset'
}));
const response = await fetch('/api/upload', {
method: 'POST',
body: formData,
credentials: 'include'
});
const result = await response.json();
console.log('File uploaded:', result.file._id);
console.log('Location:', result.file.location); // { type: 'Point', coordinates: [lng, lat] }
console.log('Witness ID:', result.file.digitalWitness.witnessId);
`
`typescript`
// After upload, trigger AI processing
const result = await client.files.processAI('file-123');
if (result.success) {
console.log('Processing started');
}
`typescript
// Create and export a binder
const binder = await client.evidenceBinders.create({
name: 'Legal Discovery',
description: 'Documents for case #12345'
});
if (binder.success) {
const exportJob = await client.evidenceBinders.export(
binder.data.binder._id,
'pdf'
);
if (exportJob.success) {
console.log('Export started:', exportJob.data.exportId);
}
}
`
The SDK includes React hooks for easier integration. Import from @witnium/vault-sdk/react:
`typescript`
import {
WitniumClientProvider,
useWitniumClient,
useFiles,
useFile,
useUpload,
useDataSources,
} from '@witnium/vault-sdk/react';
Wrap your app with the provider to use hooks:
`typescript`
function App() {
return (
);
}
Access the SDK client directly:
`typescript
function MyComponent() {
const client = useWitniumClient();
const handleClick = async () => {
const result = await client.files.list();
// ...
};
return ;
}
`
List files with automatic loading:
`typescript
function FileList() {
const { files, loading, error, refetch } = useFiles({
page: 1,
limit: 20,
path: '/documents'
});
if (loading) return
return (
$3
Handle file uploads with progress tracking:
`typescript
function UploadButton() {
const { upload, uploads, isUploading, clearCompleted } = useUpload({
onComplete: (file) => console.log('Uploaded:', file.name),
onError: (error) => console.error('Failed:', error)
}); const handleFiles = (e: React.ChangeEvent) => {
if (e.target.files) {
upload(Array.from(e.target.files), '/documents');
}
};
return (
{uploads.map(item => (
{item.file.name}: {item.progress}%
))}
);
}
`$3
Manage external data source connections:
`typescript
function DataSourcesList() {
const { dataSources, loading, connect, disconnect } = useDataSources(); return (
{dataSources.map(ds => (
{ds.provider}: {ds.status}
))}
);
}
`Development
`bash
Install dependencies
npm installBuild the SDK
npm run buildGenerate types from OpenAPI spec
npm run generate-typesType check
npm run typecheck
``MIT