Callbacks & WebhooksSecurity StandardAlgorithm: HMAC-SHA256

Webhook Security Verification

Suno API uses the HMAC-SHA256 algorithm to generate cryptographic signatures, ensuring the integrity, authenticity, and non-repudiation of webhook callbacks sent to your server.

Obtain Your Webhook HMAC Key

You can generate and view your unique webhookHmacKey in the API Keys & Webhooks Dashboard. The key is used to verify that incoming webhook requests originate from official Suno API servers. Keep this key secure and never commit it to public code repositories.

Signature Generation Formula

When dispatching a webhook callback to your specified callBackUrl, the signature is generated as follows:

signature = base64(HMAC-SHA256(taskId + "." + timestamp, webhookHmacKey))

Formula Variables:

  • taskId: The unique task ID from the callback body (data.task_id).
  • timestamp: The Unix timestamp in seconds from the X-Webhook-Timestamp request header.
  • webhookHmacKey: Your private Webhook secret key obtained from your dashboard.

Webhook Header Description

When the Webhook HMAC Key is configured, all callback requests sent to your server include the following HTTP headers:

Header FieldTypeRequiredDescription
X-Webhook-TimestampIntegerYesUnix timestamp (in seconds) when the callback request was sent by Suno API.
X-Webhook-SignatureStringYesSignature generated using HMAC-SHA256 algorithm with Base64 encoding.

Step-by-Step Verification Process

1

Read Header Fields

Extract X-Webhook-Timestamp and X-Webhook-Signature from the incoming HTTP request headers. Optionally verify that the timestamp is within 300 seconds (5 minutes) of current time to prevent replay attacks.

2

Generate Expected Signature

Extract task_id from the callback JSON body. Concatenate taskId + "." + timestamp, calculate the HMAC-SHA256 digest using your webhookHmacKey, and Base64-encode the result.

3

Constant-Time Signature Comparison

Compare the expected signature with X-Webhook-Signature using a constant-time comparison algorithm (such as Node.js crypto.timingSafeEqual or Python hmac.compare_digest) to prevent timing side-channel attacks.

Verification Implementation
const express = require('express');
const crypto = require('crypto');
const app = express();

app.use(express.json());

// Set your Webhook HMAC Key from /dashboard/keys
const WEBHOOK_HMAC_KEY = process.env.WEBHOOK_HMAC_KEY;

function generateSignature(taskId, timestampSeconds, secret) {
  // 1. Concatenate taskId and timestamp
  const dataToSign = `${taskId}.${timestampSeconds}`;

  // 2. Calculate HMAC-SHA256 signature
  const hmac = crypto.createHmac('sha256', secret);
  hmac.update(dataToSign);

  // 3. Base64 encode
  return hmac.digest('base64');
}

function verifySignature(taskId, timestampSeconds, receivedSignature, secret) {
  const expectedSignature = generateSignature(taskId, timestampSeconds, secret);

  // Constant-time length check
  if (expectedSignature.length !== receivedSignature.length) {
    return false;
  }

  // Use timingSafeEqual to prevent timing attacks
  return crypto.timingSafeEqual(
    Buffer.from(expectedSignature),
    Buffer.from(receivedSignature)
  );
}

app.post('/webhook-callback', (req, res) => {
  // 1. Read header fields
  const timestamp = req.headers['x-webhook-timestamp'];
  const receivedSignature = req.headers['x-webhook-signature'];

  if (!timestamp || !receivedSignature) {
    return res.status(401).json({ error: 'Missing signature headers' });
  }

  // 2. Extract task_id from payload
  const taskId = req.body?.data?.task_id || req.body?.taskId;
  if (!taskId) {
    return res.status(400).json({ error: 'Missing task_id' });
  }

  // 3. Verify HMAC signature
  const isValid = verifySignature(taskId, timestamp, receivedSignature, WEBHOOK_HMAC_KEY);
  if (!isValid) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // Signature verified, process callback data safely
  const { code, msg, data } = req.body;
  console.log('Received legitimate webhook request:', {
    taskId: data.task_id,
    status: code,
    callbackType: data.callbackType
  });

  return res.status(200).json({ status: 'received' });
});

app.listen(3000, () => {
  console.log('Webhook server running on port 3000');
});
Sample Webhook HTTP RequestPOST /your-endpoint
POST /your-webhook-endpoint HTTP/1.1
Host: your-server.com
Content-Type: application/json
X-Webhook-Timestamp: 1769670760
X-Webhook-Signature: KxDlpbbq0GDOKqm0+FuJpJWTzY8baHSjhEt4kwElqQI=

{
  "code": 200,
  "msg": "Success",
  "data": {
    "task_id": "ee9c2715375b7837f8bb51d641ff5863",
    "callbackType": "task_completed",
    "data": [
      {
        "id": "audio_12345678",
        "audio_url": "https://cdn.sunoapi.top/audio/track_01.mp3",
        "image_url": "https://cdn.sunoapi.top/covers/cover_01.jpg",
        "title": "Neon Horizon",
        "duration": 184.5
      }
    ]
  }
}

Manage Your Webhook Key

Retrieve your current HMAC key or regenerate a new one anytime in the Developer Console.

Go to /dashboard/keys