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​
Requirements Search​
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)​
| Endpoint | Description |
|---|---|
GET /v1/catalog/sources | List all available regulatory sources |
Protected Endpoints (API Key Required)​
| Endpoint | Description |
|---|---|
GET /v1/rules | Search rules by keyword, source, category |
GET /v1/rules/{id} | Get specific rule details |
POST /v1/semantic-search | AI-powered natural language search |
GET /v1/rules/search | Alternative 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 Code | HTTP Status | Cause | Solution |
|---|---|---|---|
INVALID_API_KEY | 401 | Invalid or expired API key | Verify your API key is correct and active |
MISSING_API_KEY | 401 | No API key provided | Include x-api-key header in requests |
RATE_LIMIT_EXCEEDED | 429 | Too many requests | Wait and retry with exponential backoff |
INSUFFICIENT_TIER | 403 | Feature requires higher tier | Upgrade your subscription |
INVALID_REQUEST | 400 | Malformed request body | Check request parameters |
NOT_FOUND | 404 | Resource doesn't exist | Verify the rule ID or endpoint path |
Debugging Tips​
-
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 -
Verify API key format: Keys should start with
ctw_prefix -
Check tier access: Some sources (ICH, EMA, WHO) require Starter tier or higher
-
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​
- npm: npmjs.com/package/@ctwise/sdk
- GitHub: github.com/ctwise/js-sdk
- Issues: github.com/ctwise/js-sdk/issues