Introdução ao Onboarding

O processo de onboarding no ConnectVets Notes é projetado para colocar sua integração funcionando rapidamente, seguindo as melhores práticas de segurança e performance para aplicações veterinárias.
Tempo estimado: 15-30 minutos para uma integração básica funcionando

Pré-requisitos

Antes de começar, certifique-se de ter:

Conta ConnectVets

Ambiente de Desenvolvimento

  • ✅ Node.js 16+ ou Python 3.8+
  • ✅ Editor de código
  • ✅ Cliente HTTP (Postman, curl, etc.)
  • ✅ Acesso à internet

Fluxo de Onboarding

Passo 1: Criar API Key

1

Acessar Dashboard

2

Ir para Configurações

No menu lateral, clique em ConfiguraçõesAPI Keys
3

Criar Nova Chave

Clique em “Nova API Key” e escolha:
  • Nome: Ex: “Integração Sistema Veterinário”
  • Tipo: write (recomendado para integração completa)
  • Ambiente: test para desenvolvimento, live para produção
4

Copiar Chave

⚠️ IMPORTANTE: A chave é exibida apenas uma vez! Copie e armazene com segurança.

Formato da API Key

# Estrutura da chave
cvn_live_1234567890abcdef1234567890abcdef

# Onde:
cvn      = Prefixo ConnectVets Notes
live     = Ambiente (live/test)  
1234...  = Token único

Passo 2: Configurar Ambiente

# .env (NUNCA commitar este arquivo!)
CONNECTVETS_API_KEY=cvn_test_sua_chave_aqui
CONNECTVETS_BASE_URL=https://api.connectvets.com
CONNECTVETS_ENVIRONMENT=development
# .env.example (pode commitar)
CONNECTVETS_API_KEY=your_api_key_here
CONNECTVETS_BASE_URL=https://api.connectvets.com
CONNECTVETS_ENVIRONMENT=development

Passo 3: Primeiro Teste

// Testar conectividade com a API
const CONNECTVETS_API_KEY = process.env.CONNECTVETS_API_KEY;
const BASE_URL = 'https://api.connectvets.com';

async function testConnection() {
  try {
    const response = await fetch(`${BASE_URL}/notes`, {
      method: 'GET',
      headers: {
        'X-API-KEY': CONNECTVETS_API_KEY,
        'Content-Type': 'application/json'
      }
    });
    
    if (response.ok) {
      const data = await response.json();
      console.log('✅ Conexão estabelecida!');
      console.log(`📋 Encontradas ${data.data.length} notas`);
      return data;
    } else {
      console.error('❌ Erro na conexão:', response.status);
    }
  } catch (error) {
    console.error('❌ Erro de rede:', error.message);
  }
}

testConnection();

Integração Básica

Enviar Primeira Nota

async function createNote(audioFile, metadata = {}) {
  const formData = new FormData();
  
  // Arquivo de áudio
  formData.append('audio', audioFile);
  
  // Metadados da consulta
  formData.append('metadata', JSON.stringify({
    patient_name: metadata.patientName || 'Teste',
    doctor_name: metadata.doctorName || 'Dr. Teste',
    consultation_date: metadata.date || new Date().toISOString(),
    species: metadata.species || 'Canino',
    ...metadata
  }));
  
  try {
    const response = await fetch(`${BASE_URL}/notes`, {
      method: 'POST',
      headers: {
        'X-API-KEY': CONNECTVETS_API_KEY
        // NÃO definir Content-Type para FormData
      },
      body: formData
    });
    
    if (response.ok) {
      const note = await response.json();
      console.log('✅ Nota criada:', note.id);
      console.log('🔄 Status:', note.status);
      return note;
    } else {
      const error = await response.json();
      console.error('❌ Erro ao criar nota:', error);
    }
  } catch (error) {
    console.error('❌ Erro de rede:', error);
  }
}

// Exemplo de uso
const audioInput = document.getElementById('audio-input');
audioInput.addEventListener('change', (event) => {
  const file = event.target.files[0];
  if (file) {
    createNote(file, {
      patientName: 'Rex',
      doctorName: 'Dr. Silva',
      species: 'Canino'
    });
  }
});

Acompanhar Processamento

async function pollNoteStatus(noteId, maxAttempts = 30) {
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      const response = await fetch(`${BASE_URL}/notes/${noteId}`, {
        headers: {
          'X-API-KEY': CONNECTVETS_API_KEY,
          'Content-Type': 'application/json'
        }
      });
      
      if (response.ok) {
        const note = await response.json();
        
        console.log(`📊 Tentativa ${attempt}: Status ${note.status}`);
        
        switch (note.status) {
          case 'completed':
            console.log('✅ Processamento concluído!');
            console.log('📝 Conteúdo:', note.sections);
            return note;
            
          case 'failed':
            console.log('❌ Processamento falhou');
            console.log('💬 Erro:', note.error_message);
            return note;
            
          case 'processing':
          case 'transcribing':
          case 'analyzing':
            console.log('⏳ Ainda processando...');
            await new Promise(resolve => setTimeout(resolve, 2000)); // Aguardar 2s
            break;
            
          default:
            console.log(`⏳ Status: ${note.status}`);
            await new Promise(resolve => setTimeout(resolve, 2000));
        }
      }
    } catch (error) {
      console.error(`❌ Erro na tentativa ${attempt}:`, error);
    }
  }
  
  console.log('⚠️ Timeout: Processamento demorou mais que esperado');
  return null;
}

// Usar após criar nota
createNote(audioFile).then(note => {
  if (note) {
    pollNoteStatus(note.id);
  }
});

Configuração de Webhooks

Para receber notificações automáticas quando as notas ficarem prontas:

1. Configurar Endpoint

// Endpoint para receber webhooks
app.post('/webhooks/connectvets', express.raw({ type: 'application/json' }), (req, res) => {
  const event = req.body;
  
  console.log('🔔 Webhook recebido:', event.type);
  
  switch (event.type) {
    case 'note.completed':
      handleNoteCompleted(event.data);
      break;
      
    case 'note.failed':
      handleNoteFailed(event.data);
      break;
      
    default:
      console.log(`❓ Evento desconhecido: ${event.type}`);
  }
  
  res.status(200).send('OK');
});

function handleNoteCompleted(noteData) {
  console.log('✅ Nota processada:', noteData.id);
  console.log('📝 Seções:', noteData.sections.length);
  
  // Integrar com seu sistema
  updatePatientRecord(noteData);
  notifyVeterinarian(noteData);
}

function handleNoteFailed(noteData) {
  console.log('❌ Falha no processamento:', noteData.id);
  console.log('💬 Erro:', noteData.error_message);
  
  // Notificar erro
  alertAdministrator(noteData);
}

2. Registrar Webhook

async function registerWebhook(url, events = ['note.completed', 'note.failed']) {
  try {
    const response = await fetch(`${BASE_URL}/webhooks`, {
      method: 'POST',
      headers: {
        'X-API-KEY': CONNECTVETS_API_KEY,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        url: url,
        events: events,
        active: true
      })
    });
    
    if (response.ok) {
      const webhook = await response.json();
      console.log('✅ Webhook registrado:', webhook.id);
      return webhook;
    }
  } catch (error) {
    console.error('❌ Erro ao registrar webhook:', error);
  }
}

// Registrar webhook
registerWebhook('https://meuapp.com/webhooks/connectvets');

Exemplo de Aplicação Completa

Setup Inicial

// config/connectvets.js
class ConnectVetsClient {
  constructor(apiKey, baseUrl = 'https://api.connectvets.com') {
    this.apiKey = apiKey;
    this.baseUrl = baseUrl;
  }
  
  async request(endpoint, options = {}) {
    const url = `${this.baseUrl}${endpoint}`;
    const config = {
      ...options,
      headers: {
        'X-API-KEY': this.apiKey,
        ...options.headers
      }
    };
    
    const response = await fetch(url, config);
    
    if (!response.ok) {
      const error = await response.json().catch(() => ({ message: 'Erro desconhecido' }));
      throw new Error(`API Error: ${response.status} - ${error.message}`);
    }
    
    return response.json();
  }
  
  async createNote(audioFile, metadata = {}) {
    const formData = new FormData();
    formData.append('audio', audioFile);
    formData.append('metadata', JSON.stringify(metadata));
    
    return this.request('/notes', {
      method: 'POST',
      body: formData,
      headers: {} // Deixar browser definir Content-Type para FormData
    });
  }
  
  async getNote(id) {
    return this.request(`/notes/${id}`);
  }
  
  async listNotes(filters = {}) {
    const params = new URLSearchParams(filters);
    return this.request(`/notes?${params}`);
  }
}

// Instanciar cliente
const connectvets = new ConnectVetsClient(process.env.CONNECTVETS_API_KEY);

module.exports = { connectvets };

Integração com Sistema Veterinário

// services/consultationService.js
const { connectvets } = require('../config/connectvets');

class ConsultationService {
  async processConsultation(consultation) {
    try {
      // 1. Preparar dados
      const metadata = {
        patient_name: consultation.patient.name,
        patient_id: consultation.patient.id,
        doctor_name: consultation.veterinarian.name,
        doctor_id: consultation.veterinarian.id,
        consultation_date: consultation.date,
        species: consultation.patient.species,
        breed: consultation.patient.breed,
        consultation_type: consultation.type
      };
      
      // 2. Enviar áudio para processamento
      console.log('📤 Enviando áudio para ConnectVets...');
      const note = await connectvets.createNote(consultation.audioFile, metadata);
      
      // 3. Salvar referência no banco
      await this.saveNoteReference(consultation.id, note.id);
      
      // 4. Iniciar monitoramento
      this.startNoteMonitoring(note.id, consultation.id);
      
      return note;
      
    } catch (error) {
      console.error('❌ Erro ao processar consulta:', error);
      throw error;
    }
  }
  
  async startNoteMonitoring(noteId, consultationId) {
    const checkStatus = async () => {
      try {
        const note = await connectvets.getNote(noteId);
        
        if (note.status === 'completed') {
          await this.handleCompletedNote(note, consultationId);
        } else if (note.status === 'failed') {
          await this.handleFailedNote(note, consultationId);
        } else {
          // Ainda processando, verificar novamente em 30s
          setTimeout(checkStatus, 30000);
        }
      } catch (error) {
        console.error('❌ Erro ao verificar status:', error);
      }
    };
    
    checkStatus();
  }
  
  async handleCompletedNote(note, consultationId) {
    console.log('✅ Nota processada para consulta:', consultationId);
    
    // Extrair informações das seções
    const sections = note.sections;
    const summary = sections.find(s => s.type === 'summary')?.content;
    const diagnosis = sections.find(s => s.type === 'diagnosis')?.content;
    const treatment = sections.find(s => s.type === 'treatment')?.content;
    
    // Atualizar prontuário
    await this.updateMedicalRecord(consultationId, {
      summary,
      diagnosis,
      treatment,
      full_transcript: note.transcript,
      connectvets_note_id: note.id
    });
    
    // Notificar veterinário
    await this.notifyVeterinarian(consultationId, note);
  }
  
  async handleFailedNote(note, consultationId) {
    console.log('❌ Falha no processamento para consulta:', consultationId);
    
    // Registrar erro
    await this.logProcessingError(consultationId, note.error_message);
    
    // Notificar administrador
    await this.notifyAdmin(consultationId, note.error_message);
  }
}

module.exports = { ConsultationService };

Checklist de Onboarding

Próximos Passos

Suporte

Precisa de ajuda durante o onboarding?
  • 📧 Email: [email protected]
  • 💬 Chat: Disponível no dashboard
  • 📖 Documentação: Sempre atualizada
  • 🐛 Bugs: Reporte via GitHub Issues