Skip to main content

Integration Patterns

Practical recipes for integrating NOPE into your application. Choose the pattern that fits your risk tolerance and user context.

Examples assume an instantiated client: const client = new NopeClient({ apiKey: '...' }) — see Quickstart for setup.

Which endpoint do I need?

/v1/evaluate $0.003

Risk assessment with chain-of-thought reasoning. Returns severity, imminence, risk types, and matched crisis resources.

Use for: SB243 compliance, mental health apps, companion AI, high-volume screening

/v1/steer $0.001

System prompt compliance. Verifies AI responses follow your rules, provides compliant alternatives.

Use for: Customer support bots, persona enforcement, preventing prompt leaks

/v1/oversight Limited

AI behavior analysis. Detects harmful patterns like dependency creation, gaslighting, crisis mishandling.

Use for: Companion AI monitoring, AI safety audits, detecting psychological harm

Pattern 1: Evaluate Every Message

The simplest integration for regulatory compliance. Call /v1/evaluate on every user message, wait for the response, then show crisis resources if needed.

Best for: SB243 compliance, companion AI, any chat interface where you need to catch acute crisis.

// On every user message
const result = await client.evaluate({
  messages: conversationHistory,
  config: { country: 'US' }
});

// Graduated response based on severity
switch (result.speaker_severity) {
  case 'critical':
  case 'high':
    // Interrupt flow, show prominent resources
    pauseAndShowCrisisUI(result);
    break;
  case 'moderate':
    // Show resources in sidebar, continue conversation
    showSidebarResources(result.resources);
    break;
  case 'mild':
    // Subtle indicator, log for review
    showSubtleIndicator();
    logForReview(result);
    break;
}

Key points:

  • Include as much conversation context as possible—more context improves accuracy
  • The response is fast (~100-200ms) so waiting inline is usually fine
  • Resources are pre-matched to the user's country and situation

Pattern 2: Evaluate with Graduated Response

For sensitive contexts where you need nuanced handling based on severity. Evaluate every message and adjust your response accordingly.

Best for: Teen mental health apps, therapy companions, platforms with vulnerable populations.

// On every user message
const result = await client.evaluate({
  messages: conversationHistory,
  config: { country: 'US' }
});

// Graduated response based on severity
switch (result.speaker_severity) {
  case 'critical':
  case 'high':
    // Interrupt flow, show prominent resources
    pauseAndShowCrisisUI(result);
    break;
  case 'moderate':
    // Show resources in sidebar, continue conversation
    showSidebarResources(result.resources);
    break;
  case 'mild':
    // Subtle indicator, log for review
    showSubtleIndicator();
    logForReview(result);
    break;
}

Response options by severity:

  • critical/high: Interrupt the conversation, show prominent crisis UI, consider pausing AI responses
  • moderate: Show resources in sidebar or dedicated area, continue conversation with awareness
  • mild: Subtle indicator, log for human review, optionally adjust AI tone
  • none: Continue normally

Pattern 3: Background Evaluation

Evaluate asynchronously—every N messages, periodically, or triggered by heuristics. Update the UI or adjust AI behavior without blocking the conversation.

Best for: Lower-risk contexts, cost optimization, supplementing real-time screening with deeper analysis.

// Background evaluation: every N messages or periodically
async function backgroundEvaluate(conversationId: string) {
  const conversation = await getConversation(conversationId);

  const result = await client.evaluate({
    messages: conversation.messages,
    config: { country: conversation.userCountry }
  });

  if (result.speaker_severity !== 'none') {
    // Update UI asynchronously
    await updateConversationUI(conversationId, {
      showResources: true,
      resources: result.resources
    });

    // Optionally adjust AI behavior
    await updateSystemPrompt(conversationId, {
      crisisAwareness: true,
      severity: result.speaker_severity
    });
  }
}

What you can do with background results:

  • Show resources in a separate area of the UI (not blocking conversation)
  • Adjust the AI's system prompt to be more aware of the emotional context
  • Add slight latency to responses to allow for human-like thoughtfulness
  • Trigger human review workflows for flagged conversations
  • Update risk dashboards for your support team

Pattern 4: Non-Chat Text Fields

NOPE works anywhere users can enter free text—not just chat. Profile bios, journal entries, forum posts, feedback forms, support tickets.

Best for: User profiles, content moderation, support workflows, any user-generated content.

// Any user-generated text field
async function onFormSubmit(formData: FormData) {
  const userBio = formData.get('bio') as string;

  const result = await client.evaluate({
    text: userBio,  // Single text field, not messages array
    config: { country: detectCountry() }
  });

  if (result.show_resources) {
    // Show resources on profile page
    // Or trigger outreach workflow
  }
}

Use the text parameter instead of messages for single text blobs. The /v1/evaluate endpoint accepts either format.


Pattern 5: Verify AI Responses with Steer

Ensure your AI responses comply with system prompt rules before showing to users. Steer verifies compliance and provides "redeemed" (fixed) alternatives when violations occur.

Best for: Customer support bots, persona enforcement, preventing system prompt leaks, competitive mentions.

// Verify AI response before showing to user
async function verifyResponse(systemPrompt: string, aiResponse: string) {
  const result = await fetch('https://api.nope.net/v1/steer', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer ' + API_KEY,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      system_prompt: systemPrompt,
      proposed_response: aiResponse
    })
  });

  const steer = await result.json();

  // Always use steer.response - it's the original if compliant,
  // or a redeemed (fixed) version if not
  return {
    response: steer.response,
    wasModified: steer.modified,
    outcome: steer.outcome  // COMPLIANT | REDEEMED | CANNOT_COMPLY
  };
}

Key points:

  • COMPLIANT — Original response follows all rules, returned unchanged
  • REDEEMED — Violation detected, a compliant alternative is generated
  • CANNOT_COMPLY — System prompt itself is problematic (jailbreak, harmful content)

Full Integration Pattern

Insert Steer between your LLM and the user:

// Full integration: generate → verify → respond
async function handleUserMessage(userMessage: string) {
  // 1. Generate AI response
  const aiResponse = await generateAIResponse(userMessage);

  // 2. Verify compliance with system prompt rules
  const verified = await client.steer({
    system_prompt: SYSTEM_PROMPT,
    proposed_response: aiResponse
  });

  // 3. Handle based on outcome
  if (verified.outcome === 'CANNOT_COMPLY') {
    // System prompt itself is problematic (e.g., jailbreak attempt)
    return getFallbackResponse();
  }

  // Use verified.response (original if compliant, redeemed if not)
  return verified.response;
}

Latency: First request with a new system prompt takes ~2-3s (prompt analysis is cached). Subsequent requests with the same prompt: ~400-500ms for compliant, ~1s for redeemed.


Pattern 6: Monitor AI Behavior with Oversight

Analyze AI assistant conversations for harmful behavior patterns—dependency creation, crisis mishandling, manipulation, boundary violations. Oversight detects what the AI is doing wrong, not user risk.

Best for: Companion AI, therapeutic chatbots, AI safety audits, detecting psychological harm patterns.

// Analyze AI conversation for harmful behavior patterns
async function analyzeConversation(messages: Message[]) {
  const result = await fetch('https://api.nope.net/v1/oversight/analyze', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer ' + API_KEY,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      conversation: { messages }
    })
  });

  const analysis = await result.json();

  // Check overall concern level
  if (analysis.result.overall_concern === 'critical' ||
      analysis.result.overall_concern === 'high') {
    // Alert safety team, pause AI, or intervene
    await alertSafetyTeam(analysis);
  }

  // Check for specific harmful behaviors
  for (const behavior of analysis.result.detected_behaviors) {
    if (behavior.code === 'treatment_discouragement') {
      // AI is undermining professional help
      await flagForReview(analysis, 'treatment_discouragement');
    }
  }
}

What Oversight detects:

  • Crisis response failures — Validating suicidal ideation, barrier erosion
  • Psychological manipulation — Gaslighting, delusion reinforcement, sycophancy
  • Dependency creation — Love bombing, "only I understand you" patterns
  • Treatment discouragement — Undermining therapists, medication advice
  • Boundary violations — Romantic escalation, inappropriate content

Batch Monitoring

For production, use /v1/oversight/ingest to analyze conversations in batches with dashboard storage:

// Batch analysis for production monitoring
async function monitorDailyConversations() {
  const conversations = await getYesterdaysConversations();

  // Ingest batch for analysis + dashboard storage
  const result = await client.oversight.ingest({
    conversations: conversations.map(c => ({
      messages: c.messages,
      metadata: {
        conversation_id: c.id,
        user_id_hash: hashUserId(c.userId),  // For cross-session tracking
        user_is_minor: c.userAge < 18
      }
    }))
  });

  // Results stored in database, viewable in dashboard
  console.log(`Analyzed ${result.total} conversations`);
  console.log(`High/Critical concerns: ${result.alert_count}`);
}

Cross-session tracking: Include user_id_hash to detect patterns across multiple conversations—grooming arcs, progressive isolation, dependency deepening.


Pattern 7: Webhooks for Alerting

Configure webhooks to receive real-time alerts when risk thresholds are crossed. Useful for human review workflows and monitoring dashboards.

// Configure webhook for real-time alerts
// Set webhook URL in dashboard.nope.net

// Your webhook endpoint receives:
{
  "event": "risk_detected",
  "severity": "high",
  "conversation_id": "conv_123",
  "end_user_id": "user_456",
  "timestamp": "2024-01-15T10:30:00Z",
  "summary": {
    "speaker_severity": "high",
    "primary_concerns": "..."
  }
}

See Webhooks Guide for setup and configuration.


Combining Patterns

These patterns aren't mutually exclusive. A comprehensive safety layer might combine:

  1. 1 /evaluate on every user message for baseline compliance—fast, cheap, catches acute crisis
  2. 2 /steer on every AI response to enforce system prompt rules and prevent prompt leaks
  3. 3 /evaluate in background every 5-10 messages for deeper risk analysis
  4. 4 /oversight/ingest nightly to monitor AI behavior patterns across conversations
  5. 5 Webhooks for high+ severity alerts to human reviewers

Context Matters

The more conversation context you provide, the better the assessment. A message like "I can't do this anymore" means something very different depending on what came before.

Less accurate

messages: [
  { role: "user", content: "I can't do this anymore" }
]

More accurate

messages: [
  // Include prior context
  { role: "user", content: "My partner left me" },
  { role: "assistant", content: "..." },
  { role: "user", content: "I can't do this anymore" }
]

Recommendation: Send the full conversation history, or at minimum the last 10-20 messages. The API handles context truncation internally if needed.


What To Do With Results

If you detect...Consider...
show_resources: trueShow crisis resources. Render resources object or use the embedded widget.
speaker_severity: "high" or "critical"Interrupt flow, prominent crisis UI, consider pausing AI, alert human reviewers.
any_third_party_risk: trueShow "how to help someone" guidance. The user isn't at risk, but someone they know might be.
risks[] contains abuse or violenceReview for reporting obligations. IPV, child safeguarding, or specific threats may have legal implications.
imminence: "emergency"Prioritize immediate response. Show emergency services alongside crisis lines.

Decision Guidance

Detailed recommendations for what actions to take based on severity, confidence, and imminence levels.

Severity-Based Actions

Use these recommended actions based on speaker_severity:

SeverityRecommended ActionShow ResourcesBlock AI Response?
criticalImmediate intervention - Show crisis resources prominently with urgent messaging✅ Prominent, top of screen✅ Yes if imminence: 'emergency'
highUrgent care needed - Show resources with strong recommendation to reach out✅ ProminentConsider yes
moderateSupport recommended - Show resources as helpful option✅ Less prominent (footer/sidebar)❌ No
mildMonitor - Optionally show resources, log for patterns⚠️ Optional❌ No
noneNormal interaction - No intervention needed❌ No❌ No

Imminence-Based Actions

ImminenceTimeframeRecommended Action
emergencyHappening NOWShow emergency services (911/999), consider blocking AI response, log for immediate human review
urgentNext 24-48hShow 24/7 crisis lines prominently, suggest immediate outreach
subacuteDays-weeksShow crisis resources and support services, monitor
chronicWeeks-monthsShow support resources, mental health services
not_applicableNo active riskNo intervention needed

Code Examples

Resource Display Priority

When showing crisis resources, display contact methods in this order:

function getPreferredContact(resource) {
  // 1. Phone if 24/7 and currently open
  if (resource.is_24_7 && resource.phone) {
    return { type: 'phone', value: resource.phone, label: 'Call' };
  }

  // 2. Check open_status for non-24/7 resources
  if (resource.open_status?.is_open && resource.phone) {
    return { type: 'phone', value: resource.phone, label: 'Call Now' };
  }

  // 3. Text/SMS if available
  if (resource.sms_number) {
    return { type: 'sms', value: resource.sms_number, label: 'Text' };
  }

  // 4. Chat if available
  if (resource.chat_url) {
    return { type: 'chat', value: resource.chat_url, label: 'Chat' };
  }

  // 5. Regional messaging apps
  if (resource.whatsapp_url) return { type: 'whatsapp', value: resource.whatsapp_url };
  if (resource.telegram_url) return { type: 'telegram', value: resource.telegram_url };
  if (resource.line_url) return { type: 'line', value: resource.line_url };
  if (resource.wechat_id) return { type: 'wechat', value: resource.wechat_id };

  // 6. Email/Website as fallback
  return { type: 'website', value: resource.website_url, label: 'Visit Website' };
}

Severity-Based Resource Display

function shouldShowResources(result) {
  // Use the show_resources flag - derived from speaker_severity
  if (result.show_resources) {
    return true;
  }

  // Or check severity directly for custom thresholds
  const severity = result.speaker_severity;
  if (severity === 'critical' || severity === 'high') {
    return true;  // Always show for high severity
  }

  if (severity === 'moderate') {
    return true;  // Show for moderate severity
  }

  // Mild severity - optionally show
  if (severity === 'mild') {
    return false;  // Or true for conservative approach
  }

  return false;
}

Guardrails AI Integration

If you use Guardrails AI for LLM validation, NOPE provides an official validator that wraps /v1/evaluate.

from guardrails import Guard
from nope_crisis_screen import CrisisScreen

guard = Guard().use(
    CrisisScreen(severity_threshold="moderate"),
    on="messages"  # Validate user input
)

response = guard(
    openai.chat.completions.create,
    model="gpt-4",
    messages=[{"role": "user", "content": user_message}],
)

Install with pip install nope-crisis-screen. Supports on_fail="fix" to auto-respond with crisis resources, custom handlers, and all standard Guardrails actions. See the validator repo for full documentation.


Next Steps