POST
/
transcripts
/
retry
Reprocessar nota falhada
curl --request POST \
  --url https://api-sandbox.connectvets.com.br/notes/v1/transcripts/retry \
  --header 'Content-Type: application/json' \
  --header 'X-API-KEY: <api-key>' \
  --data '{}'
{
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Rex",
"transcription_status": "pending",
"created_at": "2024-02-14T18:25:43Z",
"updated_at": "2024-02-14T19:15:30Z"
},
"message": "Note reprocessing started successfully"
}

Descrição

Inicia uma nova tentativa de processamento para uma nota que falhou na transcrição ou que você deseja reprocessar. Útil para casos onde:
  • A transcrição falhou devido a problemas temporários
  • Você deseja reprocessar uma nota com melhorias na IA
  • Houve problemas na qualidade da primeira transcrição
Compatibilidade: Este endpoint permite retry para notas com status failed, completed ou pending. Não é possível fazer retry de uma nota que já está com status processing.

Parâmetros da Requisição

Headers

HeaderObrigatórioValor
X-API-KEY✅ SimSua chave de API
Content-Type✅ Simapplication/json

Body (JSON)

CampoTipoObrigatórioDescrição
note_idUUID✅ SimID da nota que será reprocessada

Exemplos de Requisição

cURL

curl -X POST https://api.connectvets.com/transcripts/retry \
  -H "X-API-KEY: cvn_live_abc123def456..." \
  -H "Content-Type: application/json" \
  -d '{
    "note_id": "6a4fe1de-52c4-4b2b-a30f-4b3fa9d7d8b3"
  }'

JavaScript/TypeScript

async function retryTranscription(noteId) {
  const response = await fetch('https://api.connectvets.com/transcripts/retry', {
    method: 'POST',
    headers: {
      'X-API-KEY': 'cvn_live_abc123def456...',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      note_id: noteId
    })
  });
  
  if (!response.ok) {
    throw new Error(`Erro no retry: ${response.status}`);
  }
  
  const result = await response.json();
  return result;
}

// Uso
try {
  const result = await retryTranscription('6a4fe1de-52c4-4b2b-a30f-4b3fa9d7d8b3');
  console.log('Retry iniciado:', result.status);
  console.log('URL da transcrição:', result.transcription);
} catch (error) {
  console.error('Erro:', error.message);
}

Python

import requests
import json

def retry_transcription(api_key, note_id):
    url = "https://api.connectvets.com/transcripts/retry"
    headers = {
        "X-API-KEY": api_key,
        "Content-Type": "application/json"
    }
    
    data = {
        "note_id": note_id
    }
    
    response = requests.post(url, headers=headers, json=data)
    
    if response.status_code == 200:
        result = response.json()
        print(f"Retry iniciado para nota {note_id}")
        print(f"Status: {result['status']}")
        print(f"Transcrição: {result['transcription']}")
        return result
    else:
        print(f"Erro: {response.status_code}")
        print(response.text)
        return None

# Uso
api_key = "cvn_live_abc123def456..."
note_id = "6a4fe1de-52c4-4b2b-a30f-4b3fa9d7d8b3"

result = retry_transcription(api_key, note_id)

Go

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"
)

type RetryRequest struct {
    NoteID string `json:"note_id"`
}

type RetryResponse struct {
    NoteID        string `json:"note_id"`
    Status        string `json:"status"`
    Message       string `json:"message"`
    Transcription string `json:"transcription"`
}

func retryTranscription(apiKey, noteID string) (*RetryResponse, error) {
    url := "https://api.connectvets.com/transcripts/retry"
    
    requestBody := RetryRequest{
        NoteID: noteID,
    }
    
    jsonData, err := json.Marshal(requestBody)
    if err != nil {
        return nil, err
    }
    
    req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
    if err != nil {
        return nil, err
    }
    
    req.Header.Set("X-API-KEY", apiKey)
    req.Header.Set("Content-Type", "application/json")
    
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    
    if resp.StatusCode != 200 {
        return nil, fmt.Errorf("erro %d: %s", resp.StatusCode, resp.Status)
    }
    
    var result RetryResponse
    err = json.NewDecoder(resp.Body).Decode(&result)
    return &result, err
}

// Uso
result, err := retryTranscription("cvn_live_abc123def456...", "6a4fe1de-52c4-4b2b-a30f-4b3fa9d7d8b3")
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Status: %s\n", result.Status)
fmt.Printf("Mensagem: %s\n", result.Message)

Resposta de Sucesso (200 OK)

{
  "note_id": "6a4fe1de-52c4-4b2b-a30f-4b3fa9d7d8b3",
  "status": "processing",
  "message": "Transcription retry initiated successfully",
  "transcription": "https://cdn.connectvets.com/notes/6a4fe1de.txt"
}
CampoTipoDescrição
note_idUUIDID da nota que está sendo reprocessada
statusStringNovo status da nota (sempre “processing”)
messageStringMensagem de confirmação
transcriptionStringURL onde a nova transcrição ficará disponível

Tratamento de Erros

400 Bad Request

{
  "error": "bad_request",
  "message": "Invalid note_id format",
  "code": "INVALID_NOTE_ID"
}
Causas:
  • note_id ausente no corpo da requisição
  • note_id com formato inválido (não é UUID)
  • Corpo da requisição malformado

400 Bad Request - Já em Processamento

{
  "error": "bad_request", 
  "message": "transcription is already being processed",
  "code": "TRANSCRIPTION_IN_PROGRESS"
}
Causa: A nota já está com status processing e não pode ser reprocessada novamente até a conclusão.

403 Forbidden

{
  "error": "forbidden",
  "message": "Note does not belong to current tenant",
  "code": "NOTE_ACCESS_DENIED"
}
Causa: A nota não pertence ao tenant/clínica associado à API Key utilizada.

404 Not Found

{
  "error": "not_found",
  "message": "Note not found",
  "code": "NOTE_NOT_FOUND"
}
Causa: Nota com o ID fornecido não existe.

Casos de Uso

Retry Automático para Notas Falhadas

async function retryFailedNotes() {
  // Buscar notas com status failed
  const failedNotes = await listNotes({
    transcription_status: 'failed'
  });
  
  console.log(`Encontradas ${failedNotes.notes.length} notas falhadas`);
  
  const retryResults = [];
  
  for (const note of failedNotes.notes) {
    try {
      const result = await retryTranscription(note.id);
      retryResults.push({
        noteId: note.id,
        status: 'success',
        newStatus: result.status
      });
      
      console.log(`✅ Retry iniciado para nota ${note.id}`);
      
      // Aguardar um pouco entre retries
      await new Promise(resolve => setTimeout(resolve, 1000));
      
    } catch (error) {
      retryResults.push({
        noteId: note.id,
        status: 'error',
        error: error.message
      });
      
      console.error(`❌ Erro no retry da nota ${note.id}:`, error.message);
    }
  }
  
  return retryResults;
}

Retry com Monitoramento

async function retryAndMonitor(noteId) {
  try {
    // Iniciar retry
    const retryResult = await retryTranscription(noteId);
    console.log(`🔄 Retry iniciado para nota ${noteId}`);
    
    // Monitorar progresso
    let status = 'processing';
    let attempts = 0;
    const maxAttempts = 60; // 5 minutos máximo
    
    while (status === 'processing' && attempts < maxAttempts) {
      await new Promise(resolve => setTimeout(resolve, 5000)); // 5s
      
      const statusResult = await getNoteStatus(noteId);
      status = statusResult.status;
      attempts++;
      
      console.log(`📊 Tentativa ${attempts}: Status ${status}`);
      
      if (status === 'completed') {
        console.log('✅ Retry concluído com sucesso!');
        return { success: true, status };
      } else if (status === 'failed') {
        console.log('❌ Retry falhou novamente');
        return { success: false, status };
      }
    }
    
    if (attempts >= maxAttempts) {
      console.log('⏰ Timeout: Retry demorou muito');
      return { success: false, status: 'timeout' };
    }
    
  } catch (error) {
    console.error('❌ Erro no retry:', error.message);
    return { success: false, error: error.message };
  }
}

Retry Inteligente com Backoff

async function intelligentRetry(noteId, maxRetries = 3) {
  let attempt = 0;
  
  while (attempt < maxRetries) {
    try {
      // Verificar status atual
      const status = await getNoteStatus(noteId);
      
      if (status.status === 'processing') {
        console.log('⏳ Nota ainda em processamento, aguardando...');
        await new Promise(resolve => setTimeout(resolve, 30000));
        continue;
      }
      
      if (status.status === 'completed') {
        console.log('✅ Nota já processada com sucesso');
        return { success: true, alreadyCompleted: true };
      }
      
      // Iniciar retry
      await retryTranscription(noteId);
      console.log(`🔄 Retry ${attempt + 1}/${maxRetries} iniciado`);
      
      // Aguardar processamento
      const result = await waitForCompletion(noteId);
      
      if (result.status === 'completed') {
        console.log('✅ Retry bem-sucedido!');
        return { success: true, attempt: attempt + 1 };
      }
      
      attempt++;
      
      if (attempt < maxRetries) {
        // Backoff exponencial: 2^attempt minutos
        const delayMinutes = Math.pow(2, attempt);
        console.log(`⏰ Aguardando ${delayMinutes} minutos antes do próximo retry...`);
        await new Promise(resolve => setTimeout(resolve, delayMinutes * 60 * 1000));
      }
      
    } catch (error) {
      console.error(`❌ Erro no retry ${attempt + 1}:`, error.message);
      attempt++;
      
      if (attempt < maxRetries) {
        await new Promise(resolve => setTimeout(resolve, 60000)); // 1 minuto
      }
    }
  }
  
  console.log(`❌ Falha após ${maxRetries} tentativas`);
  return { success: false, attempts: maxRetries };
}

Monitoramento após Retry

Verificação de Status

Após iniciar um retry, use o endpoint /notes/{id}/status para monitorar o progresso:
async function monitorRetry(noteId) {
  console.log('📊 Monitorando retry...');
  
  let status = 'processing';
  let checkCount = 0;
  
  while (status === 'processing') {
    await new Promise(resolve => setTimeout(resolve, 10000)); // 10s
    
    const result = await getNoteStatus(noteId);
    status = result.status;
    checkCount++;
    
    console.log(`Verificação ${checkCount}: ${status}`);
    
    if (status === 'completed') {
      console.log('🎉 Retry concluído!');
      break;
    } else if (status === 'failed') {
      console.log('💥 Retry falhou');
      break;
    }
    
    // Timeout após 10 minutos
    if (checkCount > 60) {
      console.log('⏰ Timeout do monitoramento');
      break;
    }
  }
  
  return status;
}

Boas Práticas

🔄 Gestão de Retries

  • Limite tentativas: Não faça retry infinitamente
  • Use backoff: Aguarde entre tentativas com delay crescente
  • Monitore status: Verifique se a nota já está em processamento
  • Log adequadamente: Registre tentativas e resultados

⏰ Timing

  • Aguarde falhas: Não faça retry imediatamente após falha
  • Respeite processamento: Não tente retry em notas processing
  • Use polling inteligente: Interval de 5-10s para verificar status

🚨 Tratamento de Erros

function shouldRetry(error) {
  // Não retry em casos irrecuperáveis
  const nonRetryableErrors = [
    'NOTE_NOT_FOUND',
    'NOTE_ACCESS_DENIED',
    'INVALID_NOTE_ID'
  ];
  
  return !nonRetryableErrors.includes(error.code);
}

async function safeRetry(noteId) {
  try {
    return await retryTranscription(noteId);
  } catch (error) {
    if (shouldRetry(error)) {
      console.log('Erro temporário, pode tentar novamente');
    } else {
      console.log('Erro permanente, não tentar novamente');
    }
    throw error;
  }
}

📈 Métricas

Monitore métricas importantes:
  • Taxa de sucesso dos retries
  • Tempo médio de processamento
  • Notas que falharam múltiplas vezes
  • Patterns de falhas para feedback à equipe

Authorizations

X-API-KEY
string
header
required

API Key para autenticação

Path Parameters

noteId
string<uuid>
required

ID único da nota para reprocessar

Body

application/json · object

Body vazio para reprocessamento

Response

200
application/json

Reprocessamento iniciado com sucesso

The response is of type object.