TypeScript SDK Reference
The @curate-me/sdk package provides two main classes:
CurateGateway— configures OpenAI and Anthropic SDK clients to route through the gatewayGatewayAdmin— manages policies, secrets, API keys, and queries usage
The SDK has zero runtime dependencies — it uses the global fetch() API (Node 18+) for
admin requests and only builds configuration objects for provider SDKs.
Installation
npm install @curate-me/sdkOptional peer dependencies for provider SDKs:
npm install @curate-me/sdk openai # For OpenAI integration
npm install @curate-me/sdk @anthropic-ai/sdk # For Anthropic integrationCurateGateway
Constructor
import { CurateGateway } from '@curate-me/sdk';
const gw = new CurateGateway(
'cm_sk_your_key',
'https://api.curate-me.ai', // default
{
timeout: 60, // seconds, default
retry: { maxRetries: 2 }, // optional
},
);| Parameter | Type | Default | Description |
|---|---|---|---|
apiKey | string | required | Your Curate-Me API key (cm_sk_...) |
gatewayUrl | string | https://api.curate-me.ai | Base URL of the gateway |
options.timeout | number | 60 | Request timeout in seconds |
options.retry | GatewayRetryPolicy | 2 retries, exponential backoff | Retry policy |
openaiConfig()
Returns a configuration object for the OpenAI SDK constructor:
import OpenAI from 'openai';
const config = gw.openaiConfig('sk-your-openai-key');
const client = new OpenAI(config);
const response = await client.chat.completions.create({
model: 'gpt-4o',
messages: [{ role: 'user', content: 'Hello!' }],
});
console.log(response.choices[0].message.content);Use without a provider key to rely on a stored secret:
const client = new OpenAI(gw.openaiConfig());openai()
Convenience method that dynamically imports and returns an OpenAI client:
const client = await gw.openai('sk-your-openai-key');
// Or use a stored secret
const client = await gw.openai();
const response = await client.chat.completions.create({
model: 'gpt-4o',
messages: [{ role: 'user', content: 'Hello!' }],
});Throws an error if the openai package is not installed.
anthropicConfig()
Returns a configuration object for the Anthropic SDK constructor:
import Anthropic from '@anthropic-ai/sdk';
const config = gw.anthropicConfig('sk-ant-your-key');
const client = new Anthropic(config);
const message = await client.messages.create({
model: 'claude-sonnet-4-20250514',
max_tokens: 1024,
messages: [{ role: 'user', content: 'Hello, Claude!' }],
});
console.log(message.content[0].text);anthropic()
Convenience method that dynamically imports and returns an Anthropic client:
const client = await gw.anthropic('sk-ant-your-key');
const message = await client.messages.create({
model: 'claude-sonnet-4-20250514',
max_tokens: 1024,
messages: [{ role: 'user', content: 'Hello!' }],
});Throws an error if the @anthropic-ai/sdk package is not installed.
admin()
Returns a GatewayAdmin client for managing the gateway:
const admin = gw.admin();GatewayAdmin
The admin client uses the global fetch() API with built-in retry and typed error handling.
Constructor
import { GatewayAdmin } from '@curate-me/sdk';
const admin = new GatewayAdmin(
'cm_sk_your_key',
'https://api.curate-me.ai',
{ timeout: 30, retry: { maxRetries: 2 } },
);Or create it from a CurateGateway instance:
const admin = gw.admin();Policies
getPolicies(orgId?)
const policies = await admin.getPolicies();getPolicy(orgId)
const policy = await admin.getPolicy('org_abc123');setPolicy(orgId, policy)
Replace the governance policy for an org (PUT semantics):
await admin.setPolicy('org_abc123', {
rpm_limit: 100,
daily_budget: 50.0,
monthly_budget: 1000.0,
max_cost_per_request: 2.0,
pii_scan_enabled: true,
pii_action: 'block',
allowed_models: ['gpt-4o-mini', 'claude-haiku-3-5-20241022'],
hitl_cost_threshold: 10.0,
});updatePolicy(policy)
Create or update a policy (POST semantics):
await admin.updatePolicy({
org_id: 'org_abc123',
daily_budget: 100.0,
});deletePolicy(orgId)
Delete a policy, reverting to tier defaults:
await admin.deletePolicy('org_abc123');simulatePolicy(draftPolicy, replayHours)
Simulate a draft policy against recent traffic:
const result = await admin.simulatePolicy(
{ rpm_limit: 30, daily_budget: 10.0 },
24, // replay last 24 hours
);Usage and Costs
getUsage(options?)
const usage = await admin.getUsage({ days: 7, limit: 100 });| Option | Type | Default | Description |
|---|---|---|---|
days | number | 7 | Look-back period |
limit | number | 100 | Max records |
orgId | string | - | Org filter |
getDailyCosts(options?)
const costs = await admin.getDailyCosts({ days: 30 });getUsageRecord(requestId)
const record = await admin.getUsageRecord('gw_a1b2c3d4');Latency
getLatencyStats()
const stats = await admin.getLatencyStats();
// stats.providers.openai.p50_ms, p95_ms, p99_msAPI Keys
listApiKeys()
const keys = await admin.listApiKeys();
// keys.keys = [{key_id, name, created_at, ...}]createApiKey(name, scopes?)
const newKey = await admin.createApiKey('staging-api', ['proxy']);
// newKey.key = "cm_sk_abc..." (plaintext, shown only once)Secrets (stored provider keys)
storeSecret(provider, key, label?)
await admin.storeSecret('openai', 'sk-your-openai-key', 'Production OpenAI');listSecrets(orgId?)
const secrets = await admin.listSecrets();rotateSecret(provider, newKey)
await admin.rotateSecret('openai', 'sk-new-openai-key');revokeSecret(provider)
await admin.revokeSecret('openai');Models
listModels()
const models = await admin.listModels();
// models.data = [{id: "gpt-4o", object: "model", ...}, ...]Provider Targets
getProviderTargets(orgId?)
const targets = await admin.getProviderTargets();triggerDiscovery(providerId, apiKey?)
const result = await admin.triggerDiscovery('openai');
// result.models = ["gpt-4o", "gpt-4o-mini", ...]Discovery
getDiscoveryCatalog(options?)
const catalog = await admin.getDiscoveryCatalog({ limit: 50 });getDiscoveryHistory(options?)
const history = await admin.getDiscoveryHistory({ limit: 10 });HITL (Human-in-the-Loop)
waitForApproval(approvalId, timeout?)
Poll an approval until it is resolved or times out:
const result = await admin.waitForApproval('apr_abc123', 300);
// result.status = "approved" | "rejected" | "expired"Throws Error if not resolved within the timeout.
Policy GitOps
validatePolicyBundle(bundle, strict?)
const result = await admin.validatePolicyBundle(myBundle, true);exportPolicyBundle(orgId, version?)
const bundle = await admin.exportPolicyBundle('org_abc123');importPolicyBundle(orgId, bundle, options?)
const result = await admin.importPolicyBundle('org_abc123', myBundle, {
dryRun: true,
mergeStrategy: 'replace',
});diffPolicy(orgId, draftPolicy)
const diff = await admin.diffPolicy('org_abc123', { rpm_limit: 30 });listPolicyVersions(orgId, limit?)
const versions = await admin.listPolicyVersions('org_abc123', 10);rollbackPolicy(orgId, targetVersion, reason?)
await admin.rollbackPolicy('org_abc123', 'v3', 'Reverted budget change');GatewayRetryPolicy
Configure retry behavior:
const gw = new CurateGateway('cm_sk_your_key', 'https://api.curate-me.ai', {
retry: {
maxRetries: 3, // default: 2
initialDelay: 1.0, // seconds, default: 0.5
maxDelay: 16.0, // seconds, default: 8.0
backoffFactor: 2.0, // default: 2.0
retryableStatusCodes: [429, 500, 502, 503, 504], // defaults
},
});Error handling
The SDK throws typed errors for gateway failures:
import {
GatewayError,
GatewayAuthenticationError,
GatewayGovernanceError,
GatewayRateLimitError,
} from '@curate-me/sdk';
try {
const usage = await admin.getUsage();
} catch (err) {
if (err instanceof GatewayAuthenticationError) {
console.error(`Bad API key: ${err.message}`);
console.error(`Request ID: ${err.requestId}`);
} else if (err instanceof GatewayGovernanceError) {
console.error(`Blocked by policy: ${err.message}`);
} else if (err instanceof GatewayRateLimitError) {
console.error(`Rate limited. Retry after: ${err.retryAfter}s`);
} else if (err instanceof GatewayError) {
console.error(`Gateway error [${err.statusCode}]: ${err.message}`);
}
}| Error Class | Status | When |
|---|---|---|
GatewayAuthenticationError | 401 | Invalid or missing API key |
GatewayGovernanceError | 403 | Blocked by governance policy |
GatewayRateLimitError | 429 | Rate limit exceeded (includes retryAfter) |
GatewayError | any | Base class for all gateway errors |
All error classes include:
statusCode— HTTP status coderequestId— TheX-Request-IDfrom the gatewaybody— Raw response body text
Complete example
import { CurateGateway } from '@curate-me/sdk';
async function main() {
const gw = new CurateGateway('cm_sk_your_key');
const admin = gw.admin();
// 1. Store your OpenAI key (one-time setup)
await admin.storeSecret('openai', 'sk-your-openai-key', 'Production');
// 2. Set governance policy
await admin.setPolicy('org_abc123', {
rpm_limit: 100,
daily_budget: 50.0,
max_cost_per_request: 2.0,
pii_scan_enabled: true,
allowed_models: ['gpt-4o', 'gpt-4o-mini'],
hitl_cost_threshold: 10.0,
});
// 3. Make governed requests (no provider key needed)
const client = await gw.openai();
const response = await client.chat.completions.create({
model: 'gpt-4o',
messages: [{ role: 'user', content: 'Summarize this document...' }],
});
console.log(response.choices[0].message.content);
// 4. Check costs
const costs = await admin.getDailyCosts({ days: 7 });
for (const day of (costs as any).days || []) {
console.log(`${day.date}: $${day.total_cost.toFixed(2)}`);
}
}
main().catch(console.error);