Ferni Developers
Open Console
Developer Blog

5 Code Snippets Every Ferni Developer Needs

5 Code Snippets Every Ferni Developer Needs

Here are five code patterns you'll use constantly when building with Ferni.

1. Session Initialization

Always initialize your session with proper error handling:

import { FerniClient } from '@ferni/sdk';

const client = new FerniClient({
  apiKey: process.env.FERNI_API_KEY,
  // Enable automatic reconnection
  reconnect: true,
  reconnectAttempts: 3,
});

// Handle connection events
client.on('connected', () => console.log('Session ready'));
client.on('disconnected', (reason) => {
  console.log(`Disconnected: ${reason}`);
});

Pro tip: Set reconnect: true for production - it handles network blips gracefully.


2. Webhook Signature Verification

Never skip this step in production:

import { verifyWebhookSignature } from '@ferni/sdk';

app.post('/webhook', (req, res) => {
  const signature = req.headers['x-ferni-signature'];
  const timestamp = req.headers['x-ferni-timestamp'];

  const isValid = verifyWebhookSignature({
    payload: req.body,
    signature,
    timestamp,
    secret: process.env.WEBHOOK_SECRET,
    // Reject requests older than 5 minutes
    maxAge: 300,
  });

  if (!isValid) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // Process webhook...
});

3. Graceful Conversation Handoff

Transfer context smoothly between personas:

// In your MCP server
async function handoffToSpecialist(context, targetPersona) {
  // Preserve conversation state
  const summary = await context.summarizeConversation();

  return {
    action: 'handoff',
    target: targetPersona,
    context: {
      summary,
      userMood: context.detectedMood,
      pendingTopics: context.unaddressedTopics,
    },
  };
}

4. Streaming Response Handler

Handle voice responses without blocking:

const stream = await client.streamResponse(userInput);

for await (const chunk of stream) {
  switch (chunk.type) {
    case 'transcript':
      console.log('Speaking:', chunk.text);
      break;
    case 'emotion':
      updateAvatarExpression(chunk.emotion);
      break;
    case 'action':
      executeAction(chunk.action);
      break;
  }
}

5. Error Boundaries for Voice

Wrap voice interactions with fallbacks:

async function safeVoiceInteraction(input) {
  try {
    return await client.process(input);
  } catch (error) {
    if (error.code === 'CONTEXT_LIMIT') {
      // Summarize and continue
      await client.compactContext();
      return await client.process(input);
    }

    if (error.code === 'RATE_LIMITED') {
      // Graceful degradation
      return {
        response: "I need a moment to catch up. One second...",
        retry: true,
        retryAfter: error.retryAfter,
      };
    }

    throw error;
  }
}

Next Steps

Happy building!