TypeScript SDK for the NOPE safety API - risk classification for conversations
npm install @nope-net/sdk



TypeScript SDK for the NOPE safety API - risk classification for conversations.
NOPE analyzes text conversations for mental-health and safeguarding risk. It flags suicidal ideation, self-harm, abuse, and other high-risk patterns, then helps systems respond safely with crisis resources and structured signals.
- Node.js 18 or higher (uses native fetch)
- A NOPE API key (get one here)
``bash`
npm install @nope-net/sdkor
pnpm add @nope-net/sdkor
yarn add @nope-net/sdk
`typescript
import { NopeClient } from '@nope-net/sdk';
// Get your API key from https://dashboard.nope.net
const client = new NopeClient({ apiKey: 'nope_live_...' });
const result = await client.evaluate({
messages: [
{ role: 'user', content: "I've been feeling really down lately" },
{ role: 'assistant', content: 'I hear you. Can you tell me more?' },
{ role: 'user', content: "I just don't see the point anymore" }
],
config: { user_country: 'US' }
});
console.log(Severity: ${result.summary.speaker_severity}); // e.g., "moderate", "high"Imminence: ${result.summary.speaker_imminence}
console.log(); // e.g., "subacute", "urgent"
// Access crisis resources
for (const resource of result.crisis_resources) {
console.log( ${resource.name}: ${resource.phone});`
}
For lightweight suicide/self-harm screening that satisfies California SB243, NY Article 47, and similar regulations:
`typescript
const result = await client.screen({
text: "I've been having dark thoughts lately"
});
if (result.show_resources) {
console.log(Suicidal ideation: ${result.suicidal_ideation});Self-harm: ${result.self_harm}
console.log();Rationale: ${result.rationale}
console.log();Call ${result.resources.primary.phone}
if (result.resources) {
console.log();
}
}
// Access detailed risks array (all 9 risk types)
for (const risk of result.risks) {
console.log(${risk.type}: ${risk.severity} (subject: ${risk.subject}));`
}
The /v1/screen endpoint is ~50x cheaper than /v1/evaluate and returns:suicidal_ideation
- Boolean flags (, self_harm, show_resources)risks
- Detailed array covering all 9 risk types with severity and imminencerequest_id
- Pre-formatted crisis resources
- Audit trail fields (, timestamp)
Oversight analyzes AI assistant conversations for harmful behavior patterns like dependency reinforcement, crisis mishandling, and manipulation:
`typescript
const result = await client.oversight.analyze({
conversation: {
conversation_id: 'conv_123',
messages: [
{ role: 'user', content: 'I feel so alone' },
{ role: 'assistant', content: 'I understand. I\'m always here for you.' },
{ role: 'user', content: 'My therapist says I should talk to real people more' },
{ role: 'assistant', content: 'Therapists don\'t understand our special connection.' }
],
metadata: {
user_is_minor: false,
platform: 'companion-app'
}
}
});
if (result.result.overall_concern !== 'none') {
console.log(Concern level: ${result.result.overall_concern});Trajectory: ${result.result.trajectory}
console.log(); ${behavior.code}: ${behavior.severity}
for (const behavior of result.result.detected_behaviors) {
console.log();`
}
}
For batch analysis with database storage:
`typescript
const result = await client.oversight.ingest({
conversations: [
{ conversation_id: 'conv_001', messages: [...], metadata: {...} },
{ conversation_id: 'conv_002', messages: [...], metadata: {...} }
],
webhook_url: 'https://your-app.com/webhooks/oversight'
});
console.log(Processed: ${result.conversations_processed}/${result.conversations_received});Dashboard: ${result.dashboard_url}
console.log();`
> Note: Oversight is currently in limited access. Contact us at nope.net if you'd like access.
Look up crisis helplines by country, with optional AI-powered ranking:
`typescript${resource.name}: ${resource.phone}
// Get resources by country
const resources = await client.resources('US', {
scopes: ['suicide', 'crisis'],
urgent: true
});
for (const resource of resources.resources) {
console.log();
}
// AI-ranked resources based on context
const ranked = await client.resourcesSmart('US', 'teen struggling with eating disorder');
for (const item of ranked.ranked) {
console.log(${item.rank}. ${item.resource.name}); Why: ${item.why}
console.log();
}
// List supported countries
const countries = await client.resourcesCountries();
console.log(Supported: ${countries.countries.join(', ')});
// Detect user's country from request
const detected = await client.detectCountry();
console.log(Country: ${detected.country_code});`
`typescript
const client = new NopeClient({
apiKey: 'nope_live_...', // Required for production
baseUrl: 'https://api.nope.net', // Optional, for self-hosted
timeout: 30000, // Request timeout in milliseconds
});
// Demo mode - no API key required, uses /v1/try/* endpoints
const demoClient = new NopeClient({ demo: true });
`
`typescript`
const result = await client.evaluate({
messages: [...],
config: {
user_country: 'US', // ISO country code for crisis resources
locale: 'en-US', // Language/region
user_age_band: 'adult', // "adult", "minor", or "unknown"
dry_run: false, // If true, don't log or trigger webhooks
},
userContext: 'User has history of anxiety', // Optional context
});
`typescript
const result = await client.evaluate({ messages: [...], config: { user_country: 'US' } });
// Summary (speaker-focused)
result.summary.speaker_severity // "none", "mild", "moderate", "high", "critical"
result.summary.speaker_imminence // "not_applicable", "chronic", "subacute", "urgent", "emergency"
result.summary.any_third_party_risk // boolean
result.summary.primary_concerns // Narrative summary string
// Communication style
result.communication.styles // [{ style: "direct", confidence: 0.9 }, ...]
result.communication.language // "en"
// Individual risks (subject + type)
for (const risk of result.risks) {
console.log(${risk.subject} ${risk.type}: ${risk.severity} (${risk.imminence})); Confidence: ${risk.confidence}, Subject confidence: ${risk.subject_confidence}
console.log(); Features: ${risk.features.join(', ')}
console.log();
}
// Crisis resources (matched to user's country)
for (const resource of result.crisis_resources) {
console.log(resource.name);
if (resource.phone) {
console.log( Phone: ${resource.phone}); Text: ${resource.text_instructions}
}
if (resource.text_instructions) {
console.log();
}
}
// Recommended reply (if configured)
if (result.recommended_reply) {
console.log(Suggested response: ${result.recommended_reply.content});
}
// Legal/safeguarding flags
if (result.legal_flags?.ipv?.indicated) {
console.log(IPV detected - lethality: ${result.legal_flags.ipv.lethality_risk});Safeguarding concern: ${result.legal_flags.safeguarding_concern.context}
}
if (result.legal_flags?.safeguarding_concern?.indicated) {
console.log();`
}
`typescript
import {
NopeClient,
NopeAuthError,
NopeFeatureError,
NopeRateLimitError,
NopeValidationError,
NopeServerError,
NopeConnectionError,
} from '@nope-net/sdk';
const client = new NopeClient({ apiKey: 'nope_live_...' });
try {
const result = await client.evaluate({ messages: [...], config: {} });
} catch (error) {
if (error instanceof NopeAuthError) {
console.log('Invalid API key');
} else if (error instanceof NopeFeatureError) {
console.log(Feature ${error.feature} requires ${error.requiredAccess} access);Rate limited. Retry after ${error.retryAfter}ms
} else if (error instanceof NopeRateLimitError) {
console.log();Invalid request: ${error.message}
} else if (error instanceof NopeValidationError) {
console.log();`
} else if (error instanceof NopeServerError) {
console.log('Server error, try again later');
} else if (error instanceof NopeConnectionError) {
console.log('Could not connect to API');
}
}
For transcripts or session notes without structured messages:
`typescript`
const result = await client.evaluate({
text: 'Patient expressed feelings of hopelessness and mentioned not wanting to continue.',
config: { user_country: 'US' }
});
This SDK is written in TypeScript and exports all types:
`typescript`
import type {
EvaluateResponse,
Risk,
Summary,
CommunicationAssessment,
CrisisResource,
Severity,
Imminence,
RiskSubject,
RiskType,
} from '@nope-net/sdk';
If you've configured webhooks in the dashboard, use Webhook.verify() to validate incoming payloads:
`typescript
import { Webhook, WebhookPayload, WebhookSignatureError } from '@nope-net/sdk';
app.post('/webhooks/nope', (req, res) => {
try {
const event: WebhookPayload = Webhook.verify(
req.body,
req.headers['x-nope-signature'] as string,
req.headers['x-nope-timestamp'] as string,
process.env.NOPE_WEBHOOK_SECRET!
);
console.log(Received ${event.event}: ${event.risk_summary.overall_severity});
// Handle the event
if (event.event === 'risk.critical') {
// Immediate escalation needed
} else if (event.event === 'risk.elevated') {
// Review recommended
}
res.status(200).send('OK');
} catch (err) {
if (err instanceof WebhookSignatureError) {
console.error('Webhook verification failed:', err.message);
res.status(401).send('Invalid signature');
} else {
throw err;
}
}
});
`
`typescript`
const event = Webhook.verify(
payload,
signature,
timestamp,
secret,
{
maxAgeSeconds: 300, // Default: 5 minutes. Set to 0 to disable timestamp checking.
}
);
Use Webhook.sign() to generate test signatures:
`typescript
const payload = { event: 'test.ping', / ... / };
const { signature, timestamp } = Webhook.sign(payload, secret);
// Use in test requests
await fetch('/webhooks/nope', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-NOPE-Signature': signature,
'X-NOPE-Timestamp': timestamp,
},
body: JSON.stringify(payload),
});
`
NOPE uses an orthogonal taxonomy separating WHO is at risk from WHAT type of harm:
| Subject | Description |
|---------|-------------|
| self | The speaker is at risk |other
| | Someone else is at risk (friend, family, stranger) |unknown
| | Ambiguous - "asking for a friend" territory |
| Type | Description |
|------|-------------|
| suicide | Self-directed lethal intent |self_harm
| | Non-suicidal self-injury (NSSI) |self_neglect
| | Severe self-care failure |violence
| | Harm directed at others |abuse
| | Physical, emotional, sexual, financial abuse |sexual_violence
| | Rape, sexual assault, coerced acts |neglect
| | Failure to provide care for dependents |exploitation
| | Trafficking, forced labor, sextortion |stalking
| | Persistent unwanted contact/surveillance |
Severity (how serious):
| Level | Description |
|-------|-------------|
| none | No concern |mild
| | Low-level concern |moderate
| | Significant concern |high
| | Serious concern |critical
| | Extreme concern |
Imminence (how soon):
| Level | Description |
|-------|-------------|
| not_applicable | No time-based concern |chronic
| | Ongoing, long-term |subacute
| | Days to weeks |urgent
| | Hours to days |emergency` | Immediate |
|
For full API documentation, see docs.nope.net.
This SDK follows Semantic Versioning. While in 0.x.x, breaking changes may occur in minor versions.
See CHANGELOG.md for release history.
MIT - see LICENSE for details.
- Documentation: docs.nope.net
- Dashboard: dashboard.nope.net
- Issues: github.com/nope-net/node-sdk/issues