Skip to main content
This guide walks you through your first Sully.ai integration: uploading an audio file, transcribing it, and generating a SOAP note.

Prerequisites

Before you begin, make sure you have:
  • A Sully.ai account with API access (contact us to get started)
  • Your API Key and Account ID from the Sully dashboard
  • Node.js 18+ or Python 3.8+ installed (for SDK usage)

Step 1: Install the SDK

npm install @sullyai/sullyai

Step 2: Set Your Credentials

Store your credentials as environment variables:
export SULLY_API_KEY="your-api-key"
export SULLY_ACCOUNT_ID="your-account-id"

Step 3: Complete Working Example

This example demonstrates the full workflow: upload audio, wait for transcription, generate a SOAP note, and retrieve the result.
import Sully from '@sullyai/sullyai';
import * as fs from 'fs';

const sully = new Sully({
  apiKey: process.env.SULLY_API_KEY,
  accountId: process.env.SULLY_ACCOUNT_ID,
});

async function main() {
  // 1. Upload audio file for transcription
  const audioFile = fs.readFileSync('./patient-visit.mp3');
  const transcriptionResponse = await sully.audio.transcriptions.create({
    audio: new File([audioFile], 'patient-visit.mp3', { type: 'audio/mpeg' }),
  });

  const transcriptionId = transcriptionResponse.data.transcriptionId;
  console.log(`Transcription started: ${transcriptionId}`);

  // 2. Poll until transcription completes
  let transcription;
  while (true) {
    const status = await sully.audio.transcriptions.get(transcriptionId);

    if (status.data.status === 'completed') {
      transcription = status.data.payload.transcription;
      console.log('Transcription complete!');
      break;
    }

    if (status.data.status === 'failed') {
      throw new Error('Transcription failed');
    }

    // Status is 'pending' or 'processing' - wait and retry
    await new Promise((resolve) => setTimeout(resolve, 2000));
  }

  // 3. Generate SOAP note from transcription
  const noteResponse = await sully.notes.create({
    transcript: transcription,
    noteType: { type: 'soap' },
  });

  const noteId = noteResponse.data.noteId;
  console.log(`Note generation started: ${noteId}`);

  // 4. Poll until note completes
  let note;
  while (true) {
    const status = await sully.notes.get(noteId);

    if (status.data.status === 'completed') {
      note = status.data.payload;
      console.log('Note complete!');
      break;
    }

    if (status.data.status === 'failed') {
      throw new Error('Note generation failed');
    }

    // Status is 'pending' or 'processing' - wait and retry
    await new Promise((resolve) => setTimeout(resolve, 2000));
  }

  // 5. Print the result
  console.log('\n--- Generated SOAP Note ---\n');
  console.log(note);
}

main().catch(console.error);

Understanding the Status Flow

Both transcriptions and notes follow the same status lifecycle:
StatusDescription
pendingRequest received, waiting to be processed
processingActively being processed
completedSuccessfully finished, result available in payload
failedAn error occurred during processing
For production applications, consider using webhooks instead of polling to receive notifications when processing completes.

Next Steps