Skip to content

Audio Upload and Extension Callbacks ​

System will call this callback when audio generation is complete

When you submit an audio upload and extension task to the Suno API, you can use the callBackUrl parameter to set a callback URL. The system will automatically push the results to your specified address when the task is completed.

Callback Mechanism Overview ​

Webhook Security

To ensure the authenticity and integrity of callback requests, we strongly recommend implementing webhook signature verification. See our Webhook Verification Guide for detailed implementation steps.

Callback Timing ​

The system will send callback notifications in the following situations:

  • Text generation completed (callbackType: "text")
  • First audio track generation completed (callbackType: "first")
  • All audio tracks generation completed (callbackType: "complete")
  • Audio generation task failed
  • Errors occurred during task processing

Callbacks may be sent at the text, first and complete stages; only complete is guaranteed. Treat complete as the final result.

Callback Method ​

  • HTTP Method: POST
  • Content Type: application/json
  • Timeout Setting: 15 seconds

Callback Request Format ​

When the task progresses or completes, the system will send a POST request to your callBackUrl in the following format:

Complete Success Callback ​

json
{
  "code": 200,
  "msg": "All generated successfully.",
  "data": {
    "callbackType": "complete",
    "task_id": "2fac****9f72",
    "data": [
      {
        "id": "e231****-****-****-****-****8cadc7dc",
        "audio_url": "https://example.cn/****.mp3",
        "stream_audio_url": "https://example.cn/****",
        "image_url": "https://example.cn/****.jpeg",
        "prompt": "[Verse] Night city lights shining bright",
        "model_name": "chirp-v4-5",
        "title": "Iron Man",
        "createTime": 1786343609818,
        "duration": 198.44,
        "tags": "electrifying, rock",
        "source_audio_url":"https://example.cn/****.mp3",
        "source_image_url":"https://example.cn/****.jpeg",
        "source_stream_audio_url":"https://example.cn/****"
      },
      {
        "id": "e231****-****-****-****-****8cadc7dc",
        "audio_url": "https://example.cn/****.mp3",
        "stream_audio_url": "https://example.cn/****",
        "image_url": "https://example.cn/****.jpeg",
        "prompt": "[Verse] Night city lights shining bright",
        "model_name": "chirp-v4-5",
        "title": "Iron Man",
        "createTime": 1786343609818,
        "duration": 198.44,
        "tags": "electrifying, rock",
        "source_audio_url":"https://example.cn/****.mp3",
        "source_image_url":"https://example.cn/****.jpeg",
        "source_stream_audio_url":"https://example.cn/****"
      }
    ]
  }
}

First Track Success Callback ​

json
{
  "code": 200,
  "msg": "First audio generated successfully.",
  "data": {
    "callbackType": "first",
    "task_id": "2fac****9f72",
    "data": [
      {
        "id": "e231****-****-****-****-****8cadc7dc",
        "audio_url": "https://example.cn/****.mp3",
        "stream_audio_url": "https://example.cn/****",
        "image_url": "https://example.cn/****.jpeg",
        "prompt": "[Verse] Night city lights shining bright",
        "model_name": "chirp-v4-5",
        "title": "Iron Man",
        "createTime": 1786343609818,
        "duration": 198.44,
        "tags": "electrifying, rock",
        "source_audio_url":"https://example.cn/****.mp3",
        "source_image_url":"https://example.cn/****.jpeg",
        "source_stream_audio_url":"https://example.cn/****"
      }
    ]
  }
}

Text Generation Callback ​

json
{
  "code": 200,
  "msg": "Text generated successfully.",
  "data": {
    "callbackType": "text",
    "task_id": "2fac****9f72",
    "data": [
      {
        "id": "e231****-****-****-****-****8cadc7dc",
        "audio_url": "",
        "stream_audio_url": "https://example.cn/****",
        "image_url": "https://example.cn/****.jpeg",
        "prompt": "[Verse] Night city lights shining bright",
        "model_name": "chirp-v4-5",
        "title": "Iron Man",
        "createTime": 1786343609818,
        "tags": "electrifying, rock",
        "source_image_url":"https://example.cn/****.jpeg",
        "source_stream_audio_url":"https://example.cn/****"
      },
      {
        "id": "e231****-****-****-****-****8cadc7dc",
        "audio_url": "",
        "stream_audio_url": "https://example.cn/****",
        "image_url": "https://example.cn/****.jpeg",
        "prompt": "[Verse] Night city lights shining bright",
        "model_name": "chirp-v4-5",
        "title": "Iron Man",
        "createTime": 1786343609818,
        "tags": "electrifying, rock",
        "source_image_url":"https://example.cn/****.jpeg",
        "source_stream_audio_url":"https://example.cn/****"
      }
    ]
  }
}

Failure Callback ​

json
{
  "code": 400,
  "msg": "Audio generation failed.",
  "data": {
    "callbackType": "complete",
    "task_id": "2fac****9f72"
  }
}

On failure, code is not 200, callbackType is "complete" and data.data is absent. Check code before reading data.data.

Status Code Description ​

code (integer, required) ​

Callback status code indicating task processing result:

200 means success; any other value means failure (for example, 501).

msg (string, required) ​

Status message providing detailed status description

data.callbackType (string, required) ​

Callback type indicating the stage of generation:

  • text: Text generation complete
  • first: First track complete
  • complete: All tracks complete

data.task_id (string, required) ​

Task ID, consistent with the taskId returned when you submitted the task

data.data (array) ​

Array of generated audio data.

data.data[].id (string) ​

Unique identifier of the audio.

data.data[].audio_url (string) ​

Audio file URL.

data.data[].stream_audio_url (string) ​

Streaming audio URL.

data.data[].image_url (string) ​

Cover image URL.

data.data[].prompt (string) ​

Generation prompt or lyrics.

data.data[].model_name (string) ​

Name of the model used.

data.data[].title (string) ​

Music title.

data.data[].tags (string) ​

Music tags.

data.data[].createTime (integer) ​

Creation time.

data.data[].duration (number) ​

Audio duration in seconds.

data.data[].source_audio_url (string) ​

Source audio file URL.

data.data[].source_image_url (string) ​

Source cover image URL.

data.data[].source_stream_audio_url (string) ​

Source streaming audio URL.

Callback Reception Examples ​

Here are example codes for receiving callbacks in popular programming languages:

Node.js ​

javascript
const express = require('express');
const fs = require('fs');
const https = require('https');
const app = express();

app.use(express.json());

app.post('/suno-extend-callback', (req, res) => {
  const { code, msg, data } = req.body;
  
  console.log('Received Suno audio extension callback:', {
    taskId: data.task_id,
    callbackType: data.callbackType,
    status: code,
    message: msg
  });
  
  if (code === 200) {
    // Task progressed or completed successfully
    const { callbackType, task_id, data: tracks } = data;
    
    console.log(`Callback type: ${callbackType}`);
    console.log(`Number of tracks: ${tracks.length}`);
    
    switch (callbackType) {
      case 'text':
        console.log('Text generation completed, waiting for audio...');
        break;
        
      case 'first':
        console.log('First track completed, processing remaining tracks...');
        downloadTracks(tracks, task_id);
        break;
        
      case 'complete':
        console.log('All tracks completed successfully!');
        downloadTracks(tracks, task_id);
        break;
    }
    
  } else {
    // Task failed
    console.log('Suno audio extension failed:', msg);
    
    // Handle specific error types
    if (code === 400) {
      console.log('Validation error - check for copyrighted content');
    } else if (code === 408) {
      console.log('Rate limited - please wait before retrying');
    } else if (code === 413) {
      console.log('Content conflict - uploaded audio matches existing work');
    } else if (code === 501) {
      console.log('Generation failed - may need to adjust parameters');
    } else if (code === 531) {
      console.log('Server error with credit refund - safe to retry');
    }
  }
  
  // Return 200 status code to confirm callback received
  res.status(200).json({ status: 'received' });
});

// Function to download tracks
function downloadTracks(tracks, taskId) {
  tracks.forEach((track, index) => {
    const { 
      id, 
      audio_url, 
      source_audio_url,
      image_url, 
      source_image_url,
      title, 
      duration 
    } = track;
    
    console.log(`Track ${index + 1}: ${title} (${duration}s)`);
    
    // Download extended audio file
    if (audio_url) {
      downloadFile(audio_url, `suno_extended_${taskId}_${id}.mp3`)
        .then(() => console.log(`Extended audio downloaded: ${id}`))
        .catch(err => console.error(`Extended audio download failed for ${id}:`, err));
    }
    
    // Download source audio file
    if (source_audio_url) {
      downloadFile(source_audio_url, `suno_source_${taskId}_${id}.mp3`)
        .then(() => console.log(`Source audio downloaded: ${id}`))
        .catch(err => console.error(`Source audio download failed for ${id}:`, err));
    }
    
    // Download extended cover image
    if (image_url) {
      downloadFile(image_url, `suno_extended_cover_${taskId}_${id}.jpeg`)
        .then(() => console.log(`Extended cover downloaded: ${id}`))
        .catch(err => console.error(`Extended cover download failed for ${id}:`, err));
    }
    
    // Download source cover image
    if (source_image_url) {
      downloadFile(source_image_url, `suno_source_cover_${taskId}_${id}.jpeg`)
        .then(() => console.log(`Source cover downloaded: ${id}`))
        .catch(err => console.error(`Source cover download failed for ${id}:`, err));
    }
  });
}

// Helper function to download files
function downloadFile(url, filename) {
  return new Promise((resolve, reject) => {
    const file = fs.createWriteStream(filename);
    
    https.get(url, (response) => {
      if (response.statusCode === 200) {
        response.pipe(file);
        file.on('finish', () => {
          file.close();
          resolve();
        });
      } else {
        reject(new Error(`HTTP ${response.statusCode}`));
      }
    }).on('error', reject);
  });
}

app.listen(3000, () => {
  console.log('Callback server running on port 3000');
});

Python ​

python
from flask import Flask, request, jsonify
import requests
import os

app = Flask(__name__)

@app.route('/suno-extend-callback', methods=['POST'])
def handle_callback():
    data = request.json
    
    code = data.get('code')
    msg = data.get('msg')
    callback_data = data.get('data', {})
    callback_type = callback_data.get('callbackType')
    task_id = callback_data.get('task_id')
    tracks = callback_data.get('data', [])
    
    print(f"Received Suno audio extension callback:")
    print(f"Task ID: {task_id}, Type: {callback_type}")
    print(f"Status: {code}, Message: {msg}")
    
    if code == 200:
        # Task progressed or completed successfully
        print(f"Callback type: {callback_type}")
        print(f"Number of tracks: {len(tracks)}")
        
        if callback_type == 'text':
            print("Text generation completed, waiting for audio...")
        elif callback_type == 'first':
            print("First track completed, processing remaining tracks...")
            download_tracks(tracks, task_id)
        elif callback_type == 'complete':
            print("All tracks completed successfully!")
            download_tracks(tracks, task_id)
            
    else:
        # Task failed
        print(f"Suno audio extension failed: {msg}")
        
        # Handle specific error types
        if code == 400:
            print("Validation error - check for copyrighted content")
        elif code == 408:
            print("Rate limited - please wait before retrying")
        elif code == 413:
            print("Content conflict - uploaded audio matches existing work")
        elif code == 501:
            print("Generation failed - may need to adjust parameters")
        elif code == 531:
            print("Server error with credit refund - safe to retry")
    
    # Return 200 status code to confirm callback received
    return jsonify({'status': 'received'}), 200

def download_tracks(tracks, task_id):
    """Download audio tracks and cover images"""
    for i, track in enumerate(tracks):
        track_id = track.get('id')
        audio_url = track.get('audio_url')
        source_audio_url = track.get('source_audio_url')
        image_url = track.get('image_url')
        source_image_url = track.get('source_image_url')
        title = track.get('title')
        duration = track.get('duration')
        
        print(f"Track {i + 1}: {title} ({duration}s)")
        
        # Download extended audio file
        if audio_url:
            try:
                audio_filename = f"suno_extended_{task_id}_{track_id}.mp3"
                download_file(audio_url, audio_filename)
                print(f"Extended audio downloaded: {track_id}")
            except Exception as e:
                print(f"Extended audio download failed for {track_id}: {e}")
        
        # Download source audio file
        if source_audio_url:
            try:
                source_audio_filename = f"suno_source_{task_id}_{track_id}.mp3"
                download_file(source_audio_url, source_audio_filename)
                print(f"Source audio downloaded: {track_id}")
            except Exception as e:
                print(f"Source audio download failed for {track_id}: {e}")
        
        # Download extended cover image
        if image_url:
            try:
                image_filename = f"suno_extended_cover_{task_id}_{track_id}.jpeg"
                download_file(image_url, image_filename)
                print(f"Extended cover downloaded: {track_id}")
            except Exception as e:
                print(f"Extended cover download failed for {track_id}: {e}")
        
        # Download source cover image
        if source_image_url:
            try:
                source_image_filename = f"suno_source_cover_{task_id}_{track_id}.jpeg"
                download_file(source_image_url, source_image_filename)
                print(f"Source cover downloaded: {track_id}")
            except Exception as e:
                print(f"Source cover download failed for {track_id}: {e}")

def download_file(url, filename):
    """Download file from URL and save locally"""
    response = requests.get(url, stream=True)
    response.raise_for_status()
    
    os.makedirs('downloads', exist_ok=True)
    filepath = os.path.join('downloads', filename)
    
    with open(filepath, 'wb') as f:
        for chunk in response.iter_content(chunk_size=8192):
            f.write(chunk)

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=3000)

PHP ​

php
<?php
header('Content-Type: application/json');

// Get POST data
$input = file_get_contents('php://input');
$data = json_decode($input, true);

$code = $data['code'] ?? null;
$msg = $data['msg'] ?? '';
$callbackData = $data['data'] ?? [];
$callbackType = $callbackData['callbackType'] ?? '';
$taskId = $callbackData['task_id'] ?? '';
$tracks = $callbackData['data'] ?? [];

error_log("Received Suno audio extension callback:");
error_log("Task ID: $taskId, Type: $callbackType");
error_log("Status: $code, Message: $msg");

if ($code === 200) {
    // Task progressed or completed successfully
    error_log("Callback type: $callbackType");
    error_log("Number of tracks: " . count($tracks));
    
    switch ($callbackType) {
        case 'text':
            error_log("Text generation completed, waiting for audio...");
            break;
            
        case 'first':
            error_log("First track completed, processing remaining tracks...");
            downloadTracks($tracks, $taskId);
            break;
            
        case 'complete':
            error_log("All tracks completed successfully!");
            downloadTracks($tracks, $taskId);
            break;
    }
    
} else {
    // Task failed
    error_log("Suno audio extension failed: $msg");
    
    // Handle specific error types
    if ($code === 400) {
        error_log("Validation error - check for copyrighted content");
    } elseif ($code === 408) {
        error_log("Rate limited - please wait before retrying");
    } elseif ($code === 413) {
        error_log("Content conflict - uploaded audio matches existing work");
    } elseif ($code === 501) {
        error_log("Generation failed - may need to adjust parameters");
    } elseif ($code === 531) {
        error_log("Server error with credit refund - safe to retry");
    }
}

// Return 200 status code to confirm callback received
http_response_code(200);
echo json_encode(['status' => 'received']);

function downloadTracks($tracks, $taskId) {
    foreach ($tracks as $i => $track) {
        $trackId = $track['id'] ?? '';
        $audioUrl = $track['audio_url'] ?? '';
        $sourceAudioUrl = $track['source_audio_url'] ?? '';
        $imageUrl = $track['image_url'] ?? '';
        $sourceImageUrl = $track['source_image_url'] ?? '';
        $title = $track['title'] ?? '';
        $duration = $track['duration'] ?? 0;
        
        error_log("Track " . ($i + 1) . ": $title ({$duration}s)");
        
        // Download extended audio file
        if (!empty($audioUrl)) {
            try {
                $audioFilename = "suno_extended_{$taskId}_{$trackId}.mp3";
                downloadFile($audioUrl, $audioFilename);
                error_log("Extended audio downloaded: $trackId");
            } catch (Exception $e) {
                error_log("Extended audio download failed for $trackId: " . $e->getMessage());
            }
        }
        
        // Download source audio file
        if (!empty($sourceAudioUrl)) {
            try {
                $sourceAudioFilename = "suno_source_{$taskId}_{$trackId}.mp3";
                downloadFile($sourceAudioUrl, $sourceAudioFilename);
                error_log("Source audio downloaded: $trackId");
            } catch (Exception $e) {
                error_log("Source audio download failed for $trackId: " . $e->getMessage());
            }
        }
        
        // Download extended cover image
        if (!empty($imageUrl)) {
            try {
                $imageFilename = "suno_extended_cover_{$taskId}_{$trackId}.jpeg";
                downloadFile($imageUrl, $imageFilename);
                error_log("Extended cover downloaded: $trackId");
            } catch (Exception $e) {
                error_log("Extended cover download failed for $trackId: " . $e->getMessage());
            }
        }
        
        // Download source cover image
        if (!empty($sourceImageUrl)) {
            try {
                $sourceImageFilename = "suno_source_cover_{$taskId}_{$trackId}.jpeg";
                downloadFile($sourceImageUrl, $sourceImageFilename);
                error_log("Source cover downloaded: $trackId");
            } catch (Exception $e) {
                error_log("Source cover download failed for $trackId: " . $e->getMessage());
            }
        }
    }
}

function downloadFile($url, $filename) {
    $downloadDir = 'downloads';
    if (!is_dir($downloadDir)) {
        mkdir($downloadDir, 0755, true);
    }
    
    $filepath = $downloadDir . '/' . $filename;
    
    $fileContent = file_get_contents($url);
    if ($fileContent === false) {
        throw new Exception("Failed to download file from URL");
    }
    
    $result = file_put_contents($filepath, $fileContent);
    if ($result === false) {
        throw new Exception("Failed to save file locally");
    }
}
?>

Best Practices ​

Callback URL Configuration Recommendations

  1. Use HTTPS: Ensure your callback URL uses HTTPS protocol for secure data transmission
  2. Verify Source: Verify the legitimacy of the request source in callback processing
  3. Idempotent Processing: The same task_id may receive multiple callbacks, ensure processing logic is idempotent
  4. Quick Response: Callback processing should return a 200 status code as quickly as possible to avoid timeout
  5. Asynchronous Processing: Complex business logic should be processed asynchronously to avoid blocking callback response
  6. Handle Multiple Callbacks: Be prepared to receive text, first, and complete callbacks for the same task
  7. Download Both Versions: Consider downloading both extended and source files for comparison

Important Reminders

  • Callback URL must be a publicly accessible address
  • Server must respond within 15 seconds, otherwise it will be considered a timeout
  • If 3 consecutive retries fail, the system will stop sending callbacks
  • You may receive multiple callbacks for the same task (text → first → complete)
  • Please ensure the stability of callback processing logic to avoid callback failures due to exceptions
  • Handle copyright and conflict errors appropriately (codes 400, 413)
  • Credit refunds are automatic for certain server errors (code 531)
  • Be aware of both extended and source file URLs for complete asset management
  • Generated files will be retained for 14 days

Troubleshooting ​

If you do not receive callback notifications, please check the following:

Network Connection Issues
  • Confirm that the callback URL is accessible from the public network
  • Check firewall settings to ensure inbound requests are not blocked
  • Verify that domain name resolution is correct
Server Response Issues
  • Ensure the server returns HTTP 200 status code within 15 seconds
  • Check server logs for error messages
  • Verify that the interface path and HTTP method are correct
Content Format Issues
  • Confirm that the received POST request body is in JSON format
  • Check that Content-Type is application/json
  • Verify that JSON parsing is correct
Audio Processing Issues
  • Confirm that audio URLs are accessible
  • Check audio download permissions and network connections
  • Verify audio save paths and permissions
  • Handle both regular and streaming audio URLs appropriately
  • Process multiple tracks in the same callback
  • Download both extended and source audio files as needed
Copyright and Content Issues
  • Review error messages for copyright violations (code 400)
  • Check for content conflicts with existing works (code 413)
  • Ensure compliance with platform content policies
  • Adjust uploaded audio or prompts if flagged
  • Verify uploaded audio originality and ensure it doesn't exceed 2 minutes
Rate Limiting Issues
  • Handle timeout errors gracefully (code 408)
  • Implement appropriate retry logic with backoff
  • Monitor API usage to avoid rate limits
  • Consider upgrading service plan if needed
Extension-Specific Issues
  • Ensure uploaded audio does not exceed 2 minutes
  • Verify continueAt parameter is within valid range (greater than 0 and less than audio duration)
  • Check model version compatibility with source music
  • Monitor that extended files are properly differentiated from source files

ModelFold — one API for all the best AI models.