> ## Documentation Index
> Fetch the complete documentation index at: https://docs.akool.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook

**A webhook is an HTTP-based callback function that allows lightweight, event-driven communication between 2 application programming interfaces (APIs). Webhooks are used by a wide variety of web apps to receive small amounts of data from other apps。**

**Response Data(That is the response data that webhookUrl in the request parameter needs to give us)**
\[!NOTE] If success, http statusCode it must be 200

* **statusCode** is the http status  of response to your request . If success,it must be **200**. If you do not return a status code value of 200, we will retry the response to your webhook address.

**Response Data(The response result we give to the webhookUrl)**
**Content-Type: application/json**
**Response  Attributes**

| **Parameter** | **Type** | **Value** | **Description** |
| - | - | - | - |
| signature | String | | message body signature: signature  =sha1(sort(clientId、timestamp、nonce, dataEncrypt)) |
| dataEncrypt | String | | message body encryption, need decryption processing is required to obtain the real response data |
| timestamp | Number | | |
| nonce | String | | |

```json theme={null}
{
  "signature": "04e30dd43d9d8f95dd7c127dad617f0929d61c1d",
  "dataEncrypt": "LuG1OVSVIwOO/xpW00eSYo77Ncxa9h4VKmOJRjwoyoAmCIS/8FdJRJ+BpZn90BVAAg8xpU1bMmcDlAYDT010Wa9tNi1jivX25Ld03iA4EKs=",
  "timestamp": 1710757981609,
  "nonce": "1529"
}
```

When we complete the signature checksum and dataEncrypt decryption, we can get the real response content.
The decrypted content of dataEncrypt is:

| **Parameter** | **Type** | **Value** | **Description** |
| - | - | - | - |
| \_id | String | | \_id: returned by each interface |
| status | Number | 2  or  3  or 4 | status: the status of  image or video or faceswap or background change or avatar or   audio： 【1:queueing, 2:processing,3:completed, 4:failed】 |
| type | String | faceswap or image or audio or talking photo or video translate or background change or avatar or lipsync | Distinguish the type of each interface |
| url | String | | when staus = 3, the url  is the final result about audio, image, and video. |

**Next, we will introduce the process and methods of encryption and decryption.**

### Encryption and Decryption technology solution

The encryption and decryption technical solution is implemented based on the AES encryption and decryption algorithm, as follows:

1. clientSecret: This is the message encryption and decryption Key. The length is fixed at 24 characters. ClientSecret is used as the encryption key. \[!TIP] The clientSecret is the API Key displayed on the Website API Keys page.
2. AES adopts CBC mode, the secret key length is 24 bytes (192 bits), and the data is filled with PKCS#7; PKCS#7: K is the number of bytes of the secret key (24 is used), Buf is the content to be encrypted, N is its number of bytes. Buf needs to be filled to an integer multiple of K. Fill (K - N%K) bytes at the end of Buf, and the content of each byte is (K - N%K).
3. The IV length of AES is 16 bytes, and clientId is used as the IV.

**Message body encryption**
dataEncrypt is the result of the platform encrypting the message as follows:

* dataEncrypt = AES\_Encrypt( data, clientId, clientSecret )
  Among them, data is the body content we need to transmit, clientId is the initial vector, and clientSecret is the encryption key.

**Message body signature**
In order to verify the legitimacy of the message body, developers can verify the authenticity of the message body and decrypt the message body that passes the verification.
Specific method: dataSignature=sha1(sort(clientId、timestamp、nonce, dataEncrypt))

| **Parameter** | **Description** |
| - | - |
| clientId | clientId  of  user key pair |
| timestamp | timestamp in body |
| nonce | nonce in body |
| dataEncrypt | The previous article describes the ciphertext message body |

**Message body verification and decryption**
The developer first verifies the correctness of the message body signature, and then decrypts the message body after passing the verification.

**Ways of identifying:**

1. The developer calculates the signature,compareSignature=sha1(sort(clientId、timestamp、nonce, dataEncrypt))
2. Compare compareSignature and the signature in the body to see if they are equal. If they are equal, the verification is passed.

The decryption method is as follows:

* data = AES\_Decrypt(dataEncrypt, clientSecret);

**Example: Encryption and Decryption**

1. To use nodejs or python or java for encryption.

```javascript Nodejs theme={null}
// To use nodejs for encryption, you need to install crypto-js. Use the command npm install crypto-js to install it.
const CryptoJS = require('crypto-js')
const crypto = require('crypto');


// Generate signature
function generateMsgSignature(clientId, timestamp, nonce, msgEncrypt){
    const sortedStr = [clientId, timestamp, nonce, msgEncrypt].sort().join('');
    const hash = crypto.createHash('sha1').update(sortedStr).digest('hex');
    return hash;
}


// decryption algorithm
function generateAesDecrypt(dataEncrypt,clientId,clientSecret){
    const aesKey = clientSecret
    const key = CryptoJS.enc.Utf8.parse(aesKey)
    const iv = CryptoJS.enc.Utf8.parse(clientId)
    const decrypted = CryptoJS.AES.decrypt(dataEncrypt, key, {
        iv: iv,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    })
    return decrypted.toString(CryptoJS.enc.Utf8)
}


// Encryption Algorithm
function generateAesEncrypt(data,clientId,clientSecret){
    const aesKey = clientSecret
    const key = CryptoJS.enc.Utf8.parse(aesKey)
    const iv = CryptoJS.enc.Utf8.parse(clientId)
    const srcs = CryptoJS.enc.Utf8.parse(data)
    // CBC encryption method, Pkcs7 padding method
    const encrypted = CryptoJS.AES.encrypt(srcs, key, {
            iv: iv,
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7
    })
    return encrypted.toString()
}
```

```python Python theme={null}
import hashlib
from Crypto.Cipher import AES
import base64


# Generate signature
def generate_msg_signature(client_id, timestamp, nonce, msg_encrypt):
    sorted_str = ''.join(sorted([client_id, timestamp, nonce, msg_encrypt]))
    hash_value = hashlib.sha1(sorted_str.encode('utf-8')).hexdigest()
    return hash_value


# Decryption algorithm
def generate_aes_decrypt(data_encrypt, client_id, client_secret):
    aes_key = client_secret.encode('utf-8')

    # Ensure the IV is 16 bytes long
    iv = client_id.encode('utf-8')
    iv = iv[:16] if len(iv) >= 16 else iv.ljust(16, b'\0')

    cipher = AES.new(aes_key, AES.MODE_CBC, iv)
    decrypted_data = cipher.decrypt(base64.b64decode(data_encrypt))

    padding_len = decrypted_data[-1]
    return decrypted_data[:-padding_len].decode('utf-8')


# Encryption algorithm
def generate_aes_encrypt(data, client_id, client_secret):
    aes_key = client_secret.encode('utf-8')

    # Ensure the IV is 16 bytes long
    iv = client_id.encode('utf-8')
    iv = iv[:16] if len(iv) >= 16 else iv.ljust(16, b'\0')

    # Pkcs7 padding
    data_bytes = data.encode('utf-8')
    padding_len = AES.block_size - len(data_bytes) % AES.block_size
    padded_data = data_bytes + bytes([padding_len]) * padding_len

    cipher = AES.new(aes_key, AES.MODE_CBC, iv)
    encrypted_data = cipher.encrypt(padded_data)

    return base64.b64encode(encrypted_data).decode('utf-8')
```

```java Java theme={null}
import java.security.MessageDigest;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Arrays;
import java.nio.charset.StandardCharsets;
import javax.xml.bind.DatatypeConverter;

public class CryptoUtils {

    // Generate signature
    public static String generateMsgSignature(String clientId, String timestamp, String nonce, String msgEncrypt) {
        String[] arr = {clientId, timestamp, nonce, msgEncrypt};
        Arrays.sort(arr);
        String sortedStr = String.join("", arr);
        return sha1(sortedStr);
    }

    // SHA-1 hash function
    private static String sha1(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            byte[] hashBytes = md.digest(input.getBytes(StandardCharsets.UTF_8));
            return DatatypeConverter.printHexBinary(hashBytes).toLowerCase();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    // Decryption algorithm
    public static String generateAesDecrypt(String dataEncrypt, String clientId, String clientSecret) {
        try {
            byte[] keyBytes = clientSecret.getBytes(StandardCharsets.UTF_8);
            byte[] ivBytes = clientId.getBytes(StandardCharsets.UTF_8);

            SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
            IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);

            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);

            byte[] encryptedBytes = DatatypeConverter.parseHexBinary(dataEncrypt);
            byte[] decryptedBytes = cipher.doFinal(encryptedBytes);

            return new String(decryptedBytes, StandardCharsets.UTF_8);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    // Encryption algorithm
    public static String generateAesEncrypt(String data, String clientId, String clientSecret) {
        try {
            byte[] keyBytes = clientSecret.getBytes(StandardCharsets.UTF_8);
            byte[] ivBytes = clientId.getBytes(StandardCharsets.UTF_8);

            SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
            IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);

            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);

            byte[] encryptedBytes = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
            return DatatypeConverter.printHexBinary(encryptedBytes).toLowerCase();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    // Example usage
    public static void main(String[] args) {
        String clientId = "your_client_id";
        String clientSecret = "your_client_secret";
        String timestamp = "your_timestamp";
        String nonce = "your_nonce";
        String msgEncrypt = "your_encrypted_message";

        // Generate signature
        String signature = generateMsgSignature(clientId, timestamp, nonce, msgEncrypt);
        System.out.println("Signature: " + signature);

        // Encryption
        String data = "your_data_to_encrypt";
        String encryptedData = generateAesEncrypt(data, clientId, clientSecret);
        System.out.println("Encrypted Data: " + encryptedData);

        // Decryption
        String decryptedData = generateAesDecrypt(encryptedData, clientId, clientSecret);
        System.out.println("Decrypted Data: " + decryptedData);
    }
}

```

2. Assume that our webhookUrl has obtained the corresponding data, such as the following corresponding data

```json theme={null}
{
  "signature": "04e30dd43d9d8f95dd7c127dad617f0929d61c1d",
  "dataEncrypt": "LuG1OVSVIwOO/xpW00eSYo77Ncxa9h4VKmOJRjwoyoAmCIS/8FdJRJ+BpZn90BVAAg8xpU1bMmcDlAYDT010Wa9tNi1jivX25Ld03iA4EKs=",
  "timestamp": 1710757981609,
  "nonce": 1529
}
```

3. To verify the correctness of the signature and decrypt the content, clientId and clientSecret are required.

```javascript Nodejs theme={null}
// express example
const obj = {
  "signature": "04e30dd43d9d8f95dd7c127dad617f0929d61c1d",
  "dataEncrypt": "LuG1OVSVIwOO/xpW00eSYo77Ncxa9h4VKmOJRjwoyoAmCIS/8FdJRJ+BpZn90BVAAg8xpU1bMmcDlAYDT010Wa9tNi1jivX25Ld03iA4EKs=",
  "timestamp": 1710757981609,
  "nonce": 1529
}
 
 let clientId = "AKDt8rWEczpYPzCGur2xE="
 let clientSecret = "nmwUjMAK0PJpl0MOiXLOOOwZADm0gkLo"
 let signature = obj.signature
 let msg_encrypt = obj.dataEncrypt
 let timestamp = obj.timestamp
 let nonce = obj.nonce
 let newSignature = generateMsgSignature(clientId,timestamp,nonce,msg_encrypt)
 if (signature===newSignature) {
     let result = generateAesDecrypt(msg_encrypt,clientId,clientSecret)
     // Handle your own business logic
     response.status(200).json({})  // If the processing is successful,http statusCode:200 must be returned.
  }else {
     response.status(400).json({})
  }
```

```python Python theme={null}
import hashlib
from Crypto.Cipher import AES
import base64


def generate_msg_signature(client_id, timestamp, nonce, msg_encrypt):
    sorted_str = ''.join(sorted([client_id, timestamp, nonce, msg_encrypt]))
    hash_value = hashlib.sha1(sorted_str.encode('utf-8')).hexdigest()
    return hash_value


# Decryption algorithm
def generate_aes_decrypt(data_encrypt, client_id, client_secret):
    aes_key = client_secret.encode('utf-8')

    # Ensure the IV is 16 bytes long
    iv = client_id.encode('utf-8')
    iv = iv[:16] if len(iv) >= 16 else iv.ljust(16, b'\0')

    cipher = AES.new(aes_key, AES.MODE_CBC, iv)
    decrypted_data = cipher.decrypt(base64.b64decode(data_encrypt))

    padding_len = decrypted_data[-1]
    return decrypted_data[:-padding_len].decode('utf-8')

# Example usage
if __name__ == "__main__":
    obj = {
        "signature": "04e30dd43d9d8f95dd7c127dad617f0929d61c1d",
        "dataEncrypt": "LuG1OVSVIwOO/xpW00eSYo77Ncxa9h4VKmOJRjwoyoAmCIS/8FdJRJ+BpZn90BVAAg8xpU1bMmcDlAYDT010Wa9tNi1jivX25Ld03iA4EKs=",
        "timestamp": 1710757981609,
        "nonce": 1529
    }

    clientId = "AKDt8rWEczpYPzCGur2xE="
    clientSecret = "nmwUjMAK0PJpl0MOiXLOOOwZADm0gkLo"
    signature = obj["signature"]
    msg_encrypt = obj["dataEncrypt"]
    timestamp = obj["timestamp"]
    nonce = obj["nonce"]

    new_signature = generate_msg_signature(clientId, timestamp, nonce, msg_encrypt)
    if signature == new_signature:
        result = generate_aes_decrypt(msg_encrypt, clientId, clientSecret)
        # Handle your own business logic
        print("Decrypted Data:", result)
        # Return success http satusCode 200
        
    else:
        # Return error http statuCode 400

```

```java Java theme={null}
import java.security.MessageDigest;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

public class CryptoUtils {

    // Generate signature
    public static String generateMsgSignature(String clientId, long timestamp, int nonce, String msgEncrypt) {
        String[] arr = {clientId, String.valueOf(timestamp), String.valueOf(nonce), msgEncrypt};
        Arrays.sort(arr);
        String sortedStr = String.join("", arr);
        return sha1(sortedStr);
    }

    // SHA-1 hash function
    private static String sha1(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            byte[] hashBytes = md.digest(input.getBytes());
            StringBuilder hexString = new StringBuilder();
            for (byte b : hashBytes) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) hexString.append('0');
                hexString.append(hex);
            }
            return hexString.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    // Decryption algorithm
    public static String generateAesDecrypt(String dataEncrypt, String clientId, String clientSecret) {
        try {
            byte[] keyBytes = clientSecret.getBytes();
            byte[] ivBytes = clientId.getBytes();

            SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
            IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);

            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);

            byte[] encryptedBytes = Base64.getDecoder().decode(dataEncrypt);
            byte[] decryptedBytes = cipher.doFinal(encryptedBytes);

            return new String(decryptedBytes);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    // Example usage
    public static void main(String[] args) {
        String clientId = "AKDt8rWEczpYPzCGur2xE=";
        String clientSecret = "nmwUjMAK0PJpl0MOiXLOOOwZADm0gkLo";
        String signature = "04e30dd43d9d8f95dd7c127dad617f0929d61c1d";
        String msgEncrypt = "LuG1OVSVIwOO/xpW00eSYo77Ncxa9h4VKmOJRjwoyoAmCIS/8FdJRJ+BpZn90BVAAg8xpU1bMmcDlAYDT010Wa9tNi1jivX25Ld03iA4EKs=";
        long timestamp = 1710757981609L;
        int nonce = 1529;

        String newSignature = generateMsgSignature(clientId, timestamp, nonce, msgEncrypt);
        if (signature.equals(newSignature)) {
            String result = generateAesDecrypt(msgEncrypt, clientId, clientSecret);
            // Handle your own business logic
            System.out.println("Decrypted Data: " + result);
            // must be Return success http satusCode 200
        } else {
            // must be Return error http satusCode 400
        }
    }
}

```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.