> ## 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</Note>

* **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.</Tip>
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.

<CodeGroup>
  ```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);
      }
  }

  ```
</CodeGroup>

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.

<CodeGroup>
  ```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
          }
      }
  }

  ```
</CodeGroup>
