Skip to main content

JavaScript/TypeScript SDK

The official JavaScript/TypeScript client for CTWiseAPI.

Installation​

npm install @ctwise/sdk
# or
yarn add @ctwise/sdk
# or
pnpm add @ctwise/sdk

Requirements:

  • Node.js 16+ or modern browser
  • TypeScript 4.5+ (optional, for type safety)

Quick Start​

TypeScript​

import { CTWiseClient } from '@ctwise/sdk';

const client = new CTWiseClient({ apiKey: 'ctwise_sk_live_xxx' });

const results = await client.requirements.search({
therapeuticArea: 'Oncology',
authorities: ['FDA', 'EMA'],
phase: 'III',
});

results.requirements.forEach(req => {
console.log(`[${req.authority}] ${req.title}`);
});

JavaScript (CommonJS)​

const { CTWiseClient } = require('@ctwise/sdk');

const client = new CTWiseClient({ apiKey: 'ctwise_sk_live_xxx' });

async function main() {
const results = await client.requirements.search({
therapeuticArea: 'Oncology',
});

console.log(`Found ${results.totalCount} requirements`);
}

main();

Configuration​

Basic Configuration​

import { CTWiseClient } from '@ctwise/sdk';

const client = new CTWiseClient({
apiKey: 'ctwise_sk_live_xxx',
baseUrl: 'https://api.ctwise.ai', // Default
timeout: 30000, // Timeout in milliseconds
maxRetries: 3, // Number of retries on failure
});

Environment Variables​

export CTWISE_API_KEY="ctwise_sk_live_xxx"
import { CTWiseClient } from '@ctwise/sdk';

// API key is read from CTWISE_API_KEY
const client = new CTWiseClient();

Custom Fetch Implementation​

For environments with custom fetch requirements:

import { CTWiseClient } from '@ctwise/sdk';
import fetch from 'node-fetch';

const client = new CTWiseClient({
apiKey: 'xxx',
fetch: fetch as any, // Custom fetch implementation
});

API Reference​

Search for regulatory requirements across multiple authorities.

const results = await client.requirements.search({
// Search criteria
therapeuticArea: 'Oncology',
requirementTypes: ['endpoint_definition', 'safety_monitoring'],
authorities: ['FDA', 'ICH', 'EMA'],
phase: 'III',
keywords: ['primary endpoint', 'overall survival'],

// Options
includeGuidance: true,
includeExamples: false,
maxResults: 100,
});

// TypeScript provides full type inference
console.log(`Found ${results.totalCount} requirements`);

for (const req of results.requirements) {
console.log(req.requirementId);
console.log(req.title);
console.log(req.description);
console.log(req.implementationGuidance); // Starter tier only
}

Amendment Patterns​

Get historical amendment patterns for a therapeutic area.

const patterns = await client.patterns.get({
therapeuticArea: 'Oncology',
timePeriod: '5_years',
});

for (const pattern of patterns.patterns) {
console.log(`Pattern: ${pattern.patternType}`);
console.log(`Frequency: ${pattern.frequency}%`);
console.log(`Risk Level: ${pattern.riskLevel}`);
}

Prevention Strategies​

Get prevention strategies to avoid common protocol issues.

const strategies = await client.prevention.getStrategies({
therapeuticArea: 'Oncology',
phase: 'III',
});

for (const strategy of strategies.strategies) {
console.log(`Strategy: ${strategy.title}`);
console.log(`Effectiveness: ${strategy.effectiveness}%`);
strategy.actions.forEach(action => console.log(` - ${action}`));
}

Protocol Validation​

Validate a protocol against regulatory requirements.

const validation = await client.rules.validate({
protocol: {
title: 'Phase III Oncology Study',
therapeuticArea: 'Oncology',
phase: 'III',
primaryEndpoint: 'Overall survival',
sampleSize: 500,
},
});

console.log(`Compliance Score: ${validation.complianceScore}%`);
console.log(`Status: ${validation.status}`);

for (const issue of validation.issues) {
console.log(`[${issue.severity}] ${issue.description}`);
console.log(` Recommendation: ${issue.recommendation}`);
}

Error Handling​

import { CTWiseClient } from '@ctwise/sdk';
import {
CTWiseError,
AuthenticationError,
RateLimitError,
ValidationError,
NotFoundError,
InsufficientTierError,
} from '@ctwise/sdk/errors';

const client = new CTWiseClient({ apiKey: 'xxx' });

try {
const results = await client.requirements.search({
therapeuticArea: 'Oncology',
});
} catch (error) {
if (error instanceof AuthenticationError) {
console.error('Invalid API key');
} else if (error instanceof RateLimitError) {
console.error(`Rate limited. Retry after ${error.retryAfter} seconds`);
} else if (error instanceof InsufficientTierError) {
console.error('This feature requires a higher tier');
} else if (error instanceof ValidationError) {
console.error(`Invalid request: ${error.message}`);
} else if (error instanceof CTWiseError) {
console.error(`API error: ${error.message}`);
} else {
throw error;
}
}

TypeScript Types​

The SDK exports all types for full type safety:

import { CTWiseClient } from '@ctwise/sdk';
import type {
RequirementsSearchRequest,
RequirementsSearchResponse,
Requirement,
RegulatoryReference,
TherapeuticArea,
Authority,
Phase,
} from '@ctwise/sdk/types';

const searchRequest: RequirementsSearchRequest = {
therapeuticArea: 'Oncology' as TherapeuticArea,
authorities: ['FDA', 'EMA'] as Authority[],
phase: 'III' as Phase,
};

const results: RequirementsSearchResponse = await client.requirements.search(searchRequest);
const requirement: Requirement = results.requirements[0];
const reference: RegulatoryReference = requirement.regulatoryReference;

Pagination​

For large result sets, use pagination:

// Get first page
const firstPage = await client.requirements.search({
therapeuticArea: 'Oncology',
maxResults: 50,
});

// Async iterator for all pages
const allRequirements = [];
for await (const page of client.requirements.searchPaginated({
therapeuticArea: 'Oncology',
})) {
allRequirements.push(...page.requirements);
}

console.log(`Total requirements: ${allRequirements.length}`);

React Integration​

Hook Pattern​

import { useState, useEffect } from 'react';
import { CTWiseClient } from '@ctwise/sdk';
import type { RequirementsSearchResponse } from '@ctwise/sdk/types';

const client = new CTWiseClient({ apiKey: process.env.REACT_APP_CTWISE_API_KEY });

function useRequirements(therapeuticArea: string) {
const [data, setData] = useState<RequirementsSearchResponse | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);

useEffect(() => {
async function fetchData() {
try {
setLoading(true);
const results = await client.requirements.search({ therapeuticArea });
setData(results);
} catch (err) {
setError(err as Error);
} finally {
setLoading(false);
}
}

fetchData();
}, [therapeuticArea]);

return { data, loading, error };
}

// Usage
function RequirementsList() {
const { data, loading, error } = useRequirements('Oncology');

if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;

return (
<ul>
{data?.requirements.map(req => (
<li key={req.requirementId}>{req.title}</li>
))}
</ul>
);
}

React Query Integration​

import { useQuery } from '@tanstack/react-query';
import { CTWiseClient } from '@ctwise/sdk';

const client = new CTWiseClient({ apiKey: process.env.REACT_APP_CTWISE_API_KEY });

function useRequirements(therapeuticArea: string) {
return useQuery({
queryKey: ['requirements', therapeuticArea],
queryFn: () => client.requirements.search({ therapeuticArea }),
staleTime: 5 * 60 * 1000, // 5 minutes
});
}

Node.js Server Integration​

Express.js​

import express from 'express';
import { CTWiseClient } from '@ctwise/sdk';

const app = express();
const client = new CTWiseClient({ apiKey: process.env.CTWISE_API_KEY });

app.get('/api/requirements', async (req, res) => {
try {
const results = await client.requirements.search({
therapeuticArea: req.query.area as string,
});
res.json(results);
} catch (error) {
res.status(500).json({ error: 'Failed to fetch requirements' });
}
});

app.listen(3000);

Next.js API Route​

// pages/api/requirements.ts
import type { NextApiRequest, NextApiResponse } from 'next';
import { CTWiseClient } from '@ctwise/sdk';

const client = new CTWiseClient({ apiKey: process.env.CTWISE_API_KEY });

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const { therapeuticArea } = req.query;

try {
const results = await client.requirements.search({
therapeuticArea: therapeuticArea as string,
});
res.status(200).json(results);
} catch (error) {
res.status(500).json({ error: 'Failed to fetch requirements' });
}
}

Browser Usage​

For browser environments, use the UMD build:

<script src="https://unpkg.com/@ctwise/sdk@latest/dist/ctwise.umd.js"></script>
<script>
const client = new CTWise.CTWiseClient({ apiKey: 'xxx' });

client.requirements.search({ therapeuticArea: 'Oncology' })
.then(results => console.log(results));
</script>

Best Practices​

1. Reuse Client Instances​

// Good - singleton pattern
const client = new CTWiseClient({ apiKey: process.env.CTWISE_API_KEY });
export { client };

// Bad - create new client each time
export function search() {
const client = new CTWiseClient({ apiKey: 'xxx' });
return client.requirements.search(...);
}

2. Handle Rate Limits with Retry​

import { RateLimitError } from '@ctwise/sdk/errors';

async function searchWithRetry(params, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await client.requirements.search(params);
} catch (error) {
if (error instanceof RateLimitError && i < maxRetries - 1) {
await new Promise(r => setTimeout(r, error.retryAfter * 1000));
} else {
throw error;
}
}
}
}

3. Use Environment Variables​

// Good
const client = new CTWiseClient({
apiKey: process.env.CTWISE_API_KEY,
});

// Bad - never hardcode keys
const client = new CTWiseClient({
apiKey: 'ctwise_sk_live_xxx',
});

REST API Reference​

If you prefer to call the REST API directly without the SDK, here are the available endpoints:

Public Endpoints (No Authentication Required)​

EndpointDescription
GET /v1/catalog/sourcesList all available regulatory sources

Protected Endpoints (API Key Required)​

EndpointDescription
GET /v1/rulesSearch rules by keyword, source, category
GET /v1/rules/{id}Get specific rule details
POST /v1/semantic-searchAI-powered natural language search
GET /v1/rules/searchAlternative GET-based semantic search

Semantic Search Endpoints​

The SDK's search() method uses these endpoints under the hood:

// SDK method
const results = await client.rules.search({ query: "informed consent requirements" });

// Equivalent REST API call (POST method - recommended)
const response = await fetch("https://api.ctwise.ai/v1/semantic-search", {
method: "POST",
headers: {
"x-api-key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({ query: "informed consent requirements", limit: 10 }),
});

// Equivalent REST API call (GET method - alternative)
const getResponse = await fetch(
"https://api.ctwise.ai/v1/rules/search?q=informed+consent+requirements&limit=10",
{ headers: { "x-api-key": "YOUR_API_KEY" } }
);

Troubleshooting​

Common Errors​

Error CodeHTTP StatusCauseSolution
INVALID_API_KEY401Invalid or expired API keyVerify your API key is correct and active
MISSING_API_KEY401No API key providedInclude x-api-key header in requests
RATE_LIMIT_EXCEEDED429Too many requestsWait and retry with exponential backoff
INSUFFICIENT_TIER403Feature requires higher tierUpgrade your subscription
INVALID_REQUEST400Malformed request bodyCheck request parameters
NOT_FOUND404Resource doesn't existVerify the rule ID or endpoint path

Debugging Tips​

  1. Test connectivity with the catalog endpoint (no auth required):

    const response = await fetch("https://api.ctwise.ai/v1/catalog/sources");
    console.log(response.status); // Should be 200
  2. Verify API key format: Keys should start with ctw_ prefix

  3. Check tier access: Some sources (ICH, EMA, WHO) require Starter tier or higher

  4. Inspect error responses:

    try {
    await client.requirements.search({ therapeuticArea: "Oncology" });
    } catch (error) {
    console.log("Error code:", error.code);
    console.log("HTTP status:", error.status);
    console.log("Message:", error.message);
    }

Support​