diff --git a/.github/workflows/DNS.yml b/.github/workflows/DNS.yml
index ed0426ad..5ff1f8ab 100644
--- a/.github/workflows/DNS.yml
+++ b/.github/workflows/DNS.yml
@@ -184,7 +184,7 @@ jobs:
     - uses: actions/checkout@v2
     - name: Clone acmetest
       run: cd .. && git clone https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
-    - uses: vmactions/freebsd-vm@v0.1.2
+    - uses: vmactions/freebsd-vm@v0.1.3
       with:
         envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
         prepare: pkg install -y socat curl
diff --git a/.github/workflows/LetsEncrypt.yml b/.github/workflows/LetsEncrypt.yml
index 7c398c09..7193d88d 100644
--- a/.github/workflows/LetsEncrypt.yml
+++ b/.github/workflows/LetsEncrypt.yml
@@ -111,7 +111,7 @@ jobs:
     - uses: actions/checkout@v2
     - name: Clone acmetest
       run: cd .. && git clone https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
-    - uses: vmactions/freebsd-vm@v0.1.2
+    - uses: vmactions/freebsd-vm@v0.1.3
       with:
         envs: 'NGROK_TOKEN TEST_LOCAL'
         prepare: pkg install -y socat curl
diff --git a/acme.sh b/acme.sh
index 757ed7a5..8d422719 100755
--- a/acme.sh
+++ b/acme.sh
@@ -562,8 +562,16 @@ if _exists xargs && [ "$(printf %s '\\x41' | xargs printf)" = 'A' ]; then
 fi
 
 _h2b() {
-  if _exists xxd && xxd -r -p 2>/dev/null; then
-    return
+  if _exists xxd; then
+    if _contains "$(xxd --help 2>&1)" "assumes -c30"; then
+      if xxd -r -p -c 9999 2>/dev/null; then
+        return
+      fi
+    else
+      if xxd -r -p 2>/dev/null; then
+        return
+      fi
+    fi
   fi
 
   hex=$(cat)
@@ -1124,7 +1132,7 @@ _createkey() {
 
   if _isEccKey "$length"; then
     _debug "Using ec name: $eccname"
-    if _opkey="$(${ACME_OPENSSL_BIN:-openssl} ecparam -name "$eccname" -genkey 2>/dev/null)"; then
+    if _opkey="$(${ACME_OPENSSL_BIN:-openssl} ecparam -name "$eccname" -noout -genkey 2>/dev/null)"; then
       echo "$_opkey" >"$f"
     else
       _err "error ecc key name: $eccname"
@@ -2125,6 +2133,12 @@ _send_signed_request() {
         _sleep $_sleep_retry_sec
         continue
       fi
+      if _contains "$_body" "The Replay Nonce is not recognized"; then
+        _info "The replay Nonce is not valid, let's get a new one, Sleeping $_sleep_retry_sec seconds."
+        _CACHED_NONCE=""
+        _sleep $_sleep_retry_sec
+        continue
+      fi
     fi
     return 0
   done
@@ -5273,6 +5287,7 @@ signcsr() {
   _renew_hook="${10}"
   _local_addr="${11}"
   _challenge_alias="${12}"
+  _preferred_chain="${13}"
 
   _csrsubj=$(_readSubjectFromCSR "$_csrfile")
   if [ "$?" != "0" ]; then
@@ -5319,7 +5334,7 @@ signcsr() {
   _info "Copy csr to: $CSR_PATH"
   cp "$_csrfile" "$CSR_PATH"
 
-  issue "$_csrW" "$_csrsubj" "$_csrdomainlist" "$_csrkeylength" "$_real_cert" "$_real_key" "$_real_ca" "$_reload_cmd" "$_real_fullchain" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_addr" "$_challenge_alias"
+  issue "$_csrW" "$_csrsubj" "$_csrdomainlist" "$_csrkeylength" "$_real_cert" "$_real_key" "$_real_ca" "$_reload_cmd" "$_real_fullchain" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_addr" "$_challenge_alias" "$_preferred_chain"
 
 }
 
@@ -7416,7 +7431,7 @@ _process() {
     deploy "$_domain" "$_deploy_hook" "$_ecc"
     ;;
   signcsr)
-    signcsr "$_csr" "$_webroot" "$_cert_file" "$_key_file" "$_ca_file" "$_reloadcmd" "$_fullchain_file" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_address" "$_challenge_alias"
+    signcsr "$_csr" "$_webroot" "$_cert_file" "$_key_file" "$_ca_file" "$_reloadcmd" "$_fullchain_file" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_address" "$_challenge_alias" "$_preferred_chain"
     ;;
   showcsr)
     showcsr "$_csr" "$_domain"
diff --git a/deploy/cleverreach.sh b/deploy/cleverreach.sh
index 552d8149..a460a139 100644
--- a/deploy/cleverreach.sh
+++ b/deploy/cleverreach.sh
@@ -17,6 +17,8 @@ cleverreach_deploy() {
   _cca="$4"
   _cfullchain="$5"
 
+  _rest_endpoint="https://rest.cleverreach.com"
+
   _debug _cdomain "$_cdomain"
   _debug _ckey "$_ckey"
   _debug _ccert "$_ccert"
@@ -25,6 +27,7 @@ cleverreach_deploy() {
 
   _getdeployconf DEPLOY_CLEVERREACH_CLIENT_ID
   _getdeployconf DEPLOY_CLEVERREACH_CLIENT_SECRET
+  _getdeployconf DEPLOY_CLEVERREACH_SUBCLIENT_ID
 
   if [ -z "${DEPLOY_CLEVERREACH_CLIENT_ID}" ]; then
     _err "CleverReach Client ID is not found, please define DEPLOY_CLEVERREACH_CLIENT_ID."
@@ -37,11 +40,12 @@ cleverreach_deploy() {
 
   _savedeployconf DEPLOY_CLEVERREACH_CLIENT_ID "${DEPLOY_CLEVERREACH_CLIENT_ID}"
   _savedeployconf DEPLOY_CLEVERREACH_CLIENT_SECRET "${DEPLOY_CLEVERREACH_CLIENT_SECRET}"
+  _savedeployconf DEPLOY_CLEVERREACH_SUBCLIENT_ID "${DEPLOY_CLEVERREACH_SUBCLIENT_ID}"
 
   _info "Obtaining a CleverReach access token"
 
   _data="{\"grant_type\": \"client_credentials\", \"client_id\": \"${DEPLOY_CLEVERREACH_CLIENT_ID}\", \"client_secret\": \"${DEPLOY_CLEVERREACH_CLIENT_SECRET}\"}"
-  _auth_result="$(_post "$_data" "https://rest.cleverreach.com/oauth/token.php" "" "POST" "application/json")"
+  _auth_result="$(_post "$_data" "$_rest_endpoint/oauth/token.php" "" "POST" "application/json")"
 
   _debug _data "$_data"
   _debug _auth_result "$_auth_result"
@@ -50,14 +54,32 @@ cleverreach_deploy() {
   _debug _regex "$_regex"
   _access_token=$(echo "$_auth_result" | _json_decode | sed -n "s/$_regex/\1/p")
 
+  _debug _subclient "${DEPLOY_CLEVERREACH_SUBCLIENT_ID}"
+
+  if [ -n "${DEPLOY_CLEVERREACH_SUBCLIENT_ID}" ]; then
+    _info "Obtaining token for sub-client ${DEPLOY_CLEVERREACH_SUBCLIENT_ID}"
+    export _H1="Authorization: Bearer ${_access_token}"
+    _subclient_token_result="$(_get "$_rest_endpoint/v3/clients/$DEPLOY_CLEVERREACH_SUBCLIENT_ID/token")"
+    _access_token=$(echo "$_subclient_token_result" | sed -n "s/\"//p")
+
+    _debug _subclient_token_result "$_access_token"
+
+    _info "Destroying parent token at CleverReach, as it not needed anymore"
+    _destroy_result="$(_post "" "$_rest_endpoint/v3/oauth/token.json" "" "DELETE" "application/json")"
+    _debug _destroy_result "$_destroy_result"
+  fi
+
   _info "Uploading certificate and key to CleverReach"
 
   _certData="{\"cert\":\"$(_json_encode <"$_cfullchain")\", \"key\":\"$(_json_encode <"$_ckey")\"}"
   export _H1="Authorization: Bearer ${_access_token}"
-  _add_cert_result="$(_post "$_certData" "https://rest.cleverreach.com/v3/ssl" "" "POST" "application/json")"
+  _add_cert_result="$(_post "$_certData" "$_rest_endpoint/v3/ssl" "" "POST" "application/json")"
 
-  _debug "Destroying token at CleverReach"
-  _post "" "https://rest.cleverreach.com/v3/oauth/token.json" "" "DELETE" "application/json"
+  if [ -z "${DEPLOY_CLEVERREACH_SUBCLIENT_ID}" ]; then
+    _info "Destroying token at CleverReach, as it not needed anymore"
+    _destroy_result="$(_post "" "$_rest_endpoint/v3/oauth/token.json" "" "DELETE" "application/json")"
+    _debug _destroy_result "$_destroy_result"
+  fi
 
   if ! echo "$_add_cert_result" | grep '"error":' >/dev/null; then
     _info "Uploaded certificate successfully"
diff --git a/deploy/vault_cli.sh b/deploy/vault_cli.sh
index 8b854137..cbb8cc59 100644
--- a/deploy/vault_cli.sh
+++ b/deploy/vault_cli.sh
@@ -50,12 +50,12 @@ vault_cli_deploy() {
   fi
 
   if [ -n "$FABIO" ]; then
-    $VAULT_CMD write "${VAULT_PREFIX}/${_cdomain}" cert=@"$_cfullchain" key=@"$_ckey" || return 1
+    $VAULT_CMD kv put "${VAULT_PREFIX}/${_cdomain}" cert=@"$_cfullchain" key=@"$_ckey" || return 1
   else
-    $VAULT_CMD write "${VAULT_PREFIX}/${_cdomain}/cert.pem" value=@"$_ccert" || return 1
-    $VAULT_CMD write "${VAULT_PREFIX}/${_cdomain}/cert.key" value=@"$_ckey" || return 1
-    $VAULT_CMD write "${VAULT_PREFIX}/${_cdomain}/chain.pem" value=@"$_cca" || return 1
-    $VAULT_CMD write "${VAULT_PREFIX}/${_cdomain}/fullchain.pem" value=@"$_cfullchain" || return 1
+    $VAULT_CMD kv put "${VAULT_PREFIX}/${_cdomain}/cert.pem" value=@"$_ccert" || return 1
+    $VAULT_CMD kv put "${VAULT_PREFIX}/${_cdomain}/cert.key" value=@"$_ckey" || return 1
+    $VAULT_CMD kv put "${VAULT_PREFIX}/${_cdomain}/chain.pem" value=@"$_cca" || return 1
+    $VAULT_CMD kv put "${VAULT_PREFIX}/${_cdomain}/fullchain.pem" value=@"$_cfullchain" || return 1
   fi
 
 }
diff --git a/dnsapi/dns_constellix.sh b/dnsapi/dns_constellix.sh
index 42df710d..69d216f0 100644
--- a/dnsapi/dns_constellix.sh
+++ b/dnsapi/dns_constellix.sh
@@ -30,16 +30,41 @@ dns_constellix_add() {
     return 1
   fi
 
-  _info "Adding TXT record"
-  if _constellix_rest POST "domains/${_domain_id}/records" "[{\"type\":\"txt\",\"add\":true,\"set\":{\"name\":\"${_sub_domain}\",\"ttl\":120,\"roundRobin\":[{\"value\":\"${txtvalue}\"}]}}]"; then
-    if printf -- "%s" "$response" | grep "{\"success\":\"1 record(s) added, 0 record(s) updated, 0 record(s) deleted\"}" >/dev/null; then
-      _info "Added"
-      return 0
+  # The TXT record might already exist when working with wildcard certificates. In that case, update the record by adding the new value.
+  _debug "Search TXT record"
+  if _constellix_rest GET "domains/${_domain_id}/records/TXT/search?exact=${_sub_domain}"; then
+    if printf -- "%s" "$response" | grep "{\"errors\":\[\"Requested record was not found\"\]}" >/dev/null; then
+      _info "Adding TXT record"
+      if _constellix_rest POST "domains/${_domain_id}/records" "[{\"type\":\"txt\",\"add\":true,\"set\":{\"name\":\"${_sub_domain}\",\"ttl\":60,\"roundRobin\":[{\"value\":\"${txtvalue}\"}]}}]"; then
+        if printf -- "%s" "$response" | grep "{\"success\":\"1 record(s) added, 0 record(s) updated, 0 record(s) deleted\"}" >/dev/null; then
+          _info "Added"
+          return 0
+        else
+          _err "Error adding TXT record"
+        fi
+      fi
     else
-      _err "Error adding TXT record"
-      return 1
+      _record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[0-9]*" | cut -d ':' -f 2)
+      if _constellix_rest GET "domains/${_domain_id}/records/TXT/${_record_id}"; then
+        _new_rr_values=$(printf "%s\n" "$response" | _egrep_o '"roundRobin":\[[^]]*\]' | sed "s/\]$/,{\"value\":\"${txtvalue}\"}]/")
+        _debug _new_rr_values "$_new_rr_values"
+        _info "Updating TXT record"
+        if _constellix_rest PUT "domains/${_domain_id}/records/TXT/${_record_id}" "{\"name\":\"${_sub_domain}\",\"ttl\":60,${_new_rr_values}}"; then
+          if printf -- "%s" "$response" | grep "{\"success\":\"Record.*updated successfully\"}" >/dev/null; then
+            _info "Updated"
+            return 0
+          elif printf -- "%s" "$response" | grep "{\"errors\":\[\"Contents are identical\"\]}" >/dev/null; then
+            _info "Already exists, no need to update"
+            return 0
+          else
+            _err "Error updating TXT record"
+          fi
+        fi
+      fi
     fi
   fi
+
+  return 1
 }
 
 # Usage: fulldomain txtvalue
@@ -61,16 +86,26 @@ dns_constellix_rm() {
     return 1
   fi
 
-  _info "Removing TXT record"
-  if _constellix_rest POST "domains/${_domain_id}/records" "[{\"type\":\"txt\",\"delete\":true,\"filter\":{\"field\":\"name\",\"op\":\"eq\",\"value\":\"${_sub_domain}\"}}]"; then
-    if printf -- "%s" "$response" | grep "{\"success\":\"0 record(s) added, 0 record(s) updated, 1 record(s) deleted\"}" >/dev/null; then
+  # The TXT record might have been removed already when working with some wildcard certificates.
+  _debug "Search TXT record"
+  if _constellix_rest GET "domains/${_domain_id}/records/TXT/search?exact=${_sub_domain}"; then
+    if printf -- "%s" "$response" | grep "{\"errors\":\[\"Requested record was not found\"\]}" >/dev/null; then
       _info "Removed"
       return 0
     else
-      _err "Error removing TXT record"
-      return 1
+      _info "Removing TXT record"
+      if _constellix_rest POST "domains/${_domain_id}/records" "[{\"type\":\"txt\",\"delete\":true,\"filter\":{\"field\":\"name\",\"op\":\"eq\",\"value\":\"${_sub_domain}\"}}]"; then
+        if printf -- "%s" "$response" | grep "{\"success\":\"0 record(s) added, 0 record(s) updated, 1 record(s) deleted\"}" >/dev/null; then
+          _info "Removed"
+          return 0
+        else
+          _err "Error removing TXT record"
+        fi
+      fi
     fi
   fi
+
+  return 1
 }
 
 ####################  Private functions below ##################################
@@ -91,7 +126,7 @@ _get_root() {
     fi
 
     if _contains "$response" "\"name\":\"$h\""; then
-      _domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[0-9]+" | cut -d ':' -f 2)
+      _domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[0-9]*" | cut -d ':' -f 2)
       if [ "$_domain_id" ]; then
         _sub_domain=$(printf "%s" "$domain" | cut -d '.' -f 1-$p)
         _domain="$h"
diff --git a/dnsapi/dns_dp.sh b/dnsapi/dns_dp.sh
index 033fa5aa..9b8b7a8b 100755
--- a/dnsapi/dns_dp.sh
+++ b/dnsapi/dns_dp.sh
@@ -89,7 +89,7 @@ add_record() {
 
   _info "Adding record"
 
-  if ! _rest POST "Record.Create" "login_token=$DP_Id,$DP_Key&format=json&lang=en&domain_id=$_domain_id&sub_domain=$_sub_domain&record_type=TXT&value=$txtvalue&record_line=默认"; then
+  if ! _rest POST "Record.Create" "login_token=$DP_Id,$DP_Key&format=json&lang=en&domain_id=$_domain_id&sub_domain=$_sub_domain&record_type=TXT&value=$txtvalue&record_line=%E9%BB%98%E8%AE%A4"; then
     return 1
   fi
 
diff --git a/dnsapi/dns_ionos.sh b/dnsapi/dns_ionos.sh
index e6bd5000..aaf8580f 100755
--- a/dnsapi/dns_ionos.sh
+++ b/dnsapi/dns_ionos.sh
@@ -24,20 +24,9 @@ dns_ionos_add() {
     return 1
   fi
 
-  _new_record="{\"name\":\"$_sub_domain.$_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"ttl\":$IONOS_TXT_TTL,\"prio\":$IONOS_TXT_PRIO,\"disabled\":false}"
+  _body="[{\"name\":\"$_sub_domain.$_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"ttl\":$IONOS_TXT_TTL,\"prio\":$IONOS_TXT_PRIO,\"disabled\":false}]"
 
-  # As no POST route is supported by the API, check for existing records and include them in the PATCH request in order not delete them.
-  # This is required to support ACME v2 wildcard certificate creation, where two TXT records for the same domain name are created.
-
-  _ionos_get_existing_records "$fulldomain" "$_zone_id"
-
-  if [ "$_existing_records" ]; then
-    _body="[$_new_record,$_existing_records]"
-  else
-    _body="[$_new_record]"
-  fi
-
-  if _ionos_rest PATCH "$IONOS_ROUTE_ZONES/$_zone_id" "$_body" && [ -z "$response" ]; then
+  if _ionos_rest POST "$IONOS_ROUTE_ZONES/$_zone_id/records" "$_body" && [ -z "$response" ]; then
     _info "TXT record has been created successfully."
     return 0
   fi
@@ -125,17 +114,6 @@ _get_root() {
   return 1
 }
 
-_ionos_get_existing_records() {
-  fulldomain=$1
-  zone_id=$2
-
-  if _ionos_rest GET "$IONOS_ROUTE_ZONES/$zone_id?recordName=$fulldomain&recordType=TXT"; then
-    response="$(echo "$response" | tr -d "\n")"
-
-    _existing_records="$(printf "%s\n" "$response" | _egrep_o "\"records\":\[.*\]" | _head_n 1 | cut -d '[' -f 2 | sed 's/]//')"
-  fi
-}
-
 _ionos_get_record() {
   fulldomain=$1
   zone_id=$2
@@ -168,7 +146,7 @@ _ionos_rest() {
     export _H2="Accept: application/json"
     export _H3="Content-Type: application/json"
 
-    response="$(_post "$data" "$IONOS_API$route" "" "$method")"
+    response="$(_post "$data" "$IONOS_API$route" "" "$method" "application/json")"
   else
     export _H2="Accept: */*"
 
diff --git a/dnsapi/dns_namecheap.sh b/dnsapi/dns_namecheap.sh
index 7ce39fa9..d15d6b0e 100755
--- a/dnsapi/dns_namecheap.sh
+++ b/dnsapi/dns_namecheap.sh
@@ -208,7 +208,7 @@ _namecheap_parse_host() {
   _hostid=$(echo "$_host" | _egrep_o ' HostId="[^"]*' | cut -d '"' -f 2)
   _hostname=$(echo "$_host" | _egrep_o ' Name="[^"]*' | cut -d '"' -f 2)
   _hosttype=$(echo "$_host" | _egrep_o ' Type="[^"]*' | cut -d '"' -f 2)
-  _hostaddress=$(echo "$_host" | _egrep_o ' Address="[^"]*' | cut -d '"' -f 2)
+  _hostaddress=$(echo "$_host" | _egrep_o ' Address="[^"]*' | cut -d '"' -f 2 | _xml_decode)
   _hostmxpref=$(echo "$_host" | _egrep_o ' MXPref="[^"]*' | cut -d '"' -f 2)
   _hostttl=$(echo "$_host" | _egrep_o ' TTL="[^"]*' | cut -d '"' -f 2)
 
@@ -405,3 +405,7 @@ _namecheap_set_tld_sld() {
   done
 
 }
+
+_xml_decode() {
+  sed 's/&quot;/"/g'
+}
diff --git a/dnsapi/dns_porkbun.sh b/dnsapi/dns_porkbun.sh
new file mode 100644
index 00000000..18da6b2f
--- /dev/null
+++ b/dnsapi/dns_porkbun.sh
@@ -0,0 +1,157 @@
+#!/usr/bin/env sh
+
+#
+#PORKBUN_API_KEY="pk1_0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
+#PORKBUN_SECRET_API_KEY="sk1_0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
+
+PORKBUN_Api="https://porkbun.com/api/json/v3"
+
+########  Public functions #####################
+
+#Usage: add  _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_porkbun_add() {
+  fulldomain=$1
+  txtvalue=$2
+
+  PORKBUN_API_KEY="${PORKBUN_API_KEY:-$(_readaccountconf_mutable PORKBUN_API_KEY)}"
+  PORKBUN_SECRET_API_KEY="${PORKBUN_SECRET_API_KEY:-$(_readaccountconf_mutable PORKBUN_SECRET_API_KEY)}"
+
+  if [ -z "$PORKBUN_API_KEY" ] || [ -z "$PORKBUN_SECRET_API_KEY" ]; then
+    PORKBUN_API_KEY=''
+    PORKBUN_SECRET_API_KEY=''
+    _err "You didn't specify a Porkbun api key and secret api key yet."
+    _err "You can get yours from here https://porkbun.com/account/api."
+    return 1
+  fi
+
+  #save the credentials to the account conf file.
+  _saveaccountconf_mutable PORKBUN_API_KEY "$PORKBUN_API_KEY"
+  _saveaccountconf_mutable PORKBUN_SECRET_API_KEY "$PORKBUN_SECRET_API_KEY"
+
+  _debug 'First detect the root zone'
+  if ! _get_root "$fulldomain"; then
+    return 1
+  fi
+  _debug _sub_domain "$_sub_domain"
+  _debug _domain "$_domain"
+
+  # For wildcard cert, the main root domain and the wildcard domain have the same txt subdomain name, so
+  # we can not use updating anymore.
+  #  count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2)
+  #  _debug count "$count"
+  #  if [ "$count" = "0" ]; then
+  _info "Adding record"
+  if _porkbun_rest POST "dns/create/$_domain" "{\"name\":\"$_sub_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"ttl\":120}"; then
+    if _contains "$response" '\"status\":"SUCCESS"'; then
+      _info "Added, OK"
+      return 0
+    elif _contains "$response" "The record already exists"; then
+      _info "Already exists, OK"
+      return 0
+    else
+      _err "Add txt record error. ($response)"
+      return 1
+    fi
+  fi
+  _err "Add txt record error."
+  return 1
+
+}
+
+#fulldomain txtvalue
+dns_porkbun_rm() {
+  fulldomain=$1
+  txtvalue=$2
+
+  PORKBUN_API_KEY="${PORKBUN_API_KEY:-$(_readaccountconf_mutable PORKBUN_API_KEY)}"
+  PORKBUN_SECRET_API_KEY="${PORKBUN_SECRET_API_KEY:-$(_readaccountconf_mutable PORKBUN_SECRET_API_KEY)}"
+
+  _debug 'First detect the root zone'
+  if ! _get_root "$fulldomain"; then
+    return 1
+  fi
+  _debug _sub_domain "$_sub_domain"
+  _debug _domain "$_domain"
+
+  count=$(echo "$response" | _egrep_o "\"count\": *[^,]*" | cut -d : -f 2 | tr -d " ")
+  _debug count "$count"
+  if [ "$count" = "0" ]; then
+    _info "Don't need to remove."
+  else
+    record_id=$(echo "$response" | tr '{' '\n' | grep "$txtvalue" | cut -d, -f1 | cut -d: -f2 | tr -d \")
+    _debug "record_id" "$record_id"
+    if [ -z "$record_id" ]; then
+      _err "Can not get record id to remove."
+      return 1
+    fi
+    if ! _porkbun_rest POST "dns/delete/$_domain/$record_id"; then
+      _err "Delete record error."
+      return 1
+    fi
+    echo "$response" | tr -d " " | grep '\"status\":"SUCCESS"' >/dev/null
+  fi
+
+}
+
+####################  Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+_get_root() {
+  domain=$1
+  i=1
+  while true; do
+    h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+    _debug h "$h"
+    if [ -z "$h" ]; then
+      return 1
+    fi
+
+    if _porkbun_rest POST "dns/retrieve/$h"; then
+      if _contains "$response" "\"status\":\"SUCCESS\""; then
+        _sub_domain="$(echo "$fulldomain" | sed "s/\\.$_domain\$//")"
+        _domain=$h
+        return 0
+      else
+        _debug "Go to next level of $_domain"
+      fi
+    else
+      _debug "Go to next level of $_domain"
+    fi
+    i=$(_math "$i" + 1)
+  done
+
+  return 1
+}
+
+_porkbun_rest() {
+  m=$1
+  ep="$2"
+  data="$3"
+  _debug "$ep"
+
+  api_key_trimmed=$(echo "$PORKBUN_API_KEY" | tr -d '"')
+  secret_api_key_trimmed=$(echo "$PORKBUN_SECRET_API_KEY" | tr -d '"')
+
+  test -z "$data" && data="{" || data="$(echo $data | cut -d'}' -f1),"
+  data="$data\"apikey\":\"$api_key_trimmed\",\"secretapikey\":\"$secret_api_key_trimmed\"}"
+
+  export _H1="Content-Type: application/json"
+
+  if [ "$m" != "GET" ]; then
+    _debug data "$data"
+    response="$(_post "$data" "$PORKBUN_Api/$ep" "" "$m")"
+  else
+    response="$(_get "$PORKBUN_Api/$ep")"
+  fi
+
+  _sleep 3 # prevent rate limit
+
+  if [ "$?" != "0" ]; then
+    _err "error $ep"
+    return 1
+  fi
+  _debug2 response "$response"
+  return 0
+}
diff --git a/dnsapi/dns_servercow.sh b/dnsapi/dns_servercow.sh
index e73d85b0..f70a2294 100755
--- a/dnsapi/dns_servercow.sh
+++ b/dnsapi/dns_servercow.sh
@@ -49,16 +49,42 @@ dns_servercow_add() {
   _debug _sub_domain "$_sub_domain"
   _debug _domain "$_domain"
 
-  if _servercow_api POST "$_domain" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":20}"; then
-    if printf -- "%s" "$response" | grep "ok" >/dev/null; then
-      _info "Added, OK"
-      return 0
-    else
-      _err "add txt record error."
-      return 1
+  # check whether a txt record already exists for the subdomain
+  if printf -- "%s" "$response" | grep "{\"name\":\"$_sub_domain\",\"ttl\":20,\"type\":\"TXT\"" >/dev/null; then
+    _info "A txt record with the same name already exists."
+    # trim the string on the left
+    txtvalue_old=${response#*{\"name\":\"$_sub_domain\",\"ttl\":20,\"type\":\"TXT\",\"content\":\"}
+    # trim the string on the right
+    txtvalue_old=${txtvalue_old%%\"*}
+
+    _debug txtvalue_old "$txtvalue_old"
+
+    _info "Add the new txtvalue to the existing txt record."
+    if _servercow_api POST "$_domain" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":[\"$txtvalue\",\"$txtvalue_old\"],\"ttl\":20}"; then
+      if printf -- "%s" "$response" | grep "ok" >/dev/null; then
+        _info "Added additional txtvalue, OK"
+        return 0
+      else
+        _err "add txt record error."
+        return 1
+      fi
     fi
+    _err "add txt record error."
+    return 1
+  else
+    _info "There is no txt record with the name yet."
+    if _servercow_api POST "$_domain" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":20}"; then
+      if printf -- "%s" "$response" | grep "ok" >/dev/null; then
+        _info "Added, OK"
+        return 0
+      else
+        _err "add txt record error."
+        return 1
+      fi
+    fi
+    _err "add txt record error."
+    return 1
   fi
-  _err "add txt record error."
 
   return 1
 }
diff --git a/dnsapi/dns_simply.sh b/dnsapi/dns_simply.sh
index d053dcf6..e0e05017 100644
--- a/dnsapi/dns_simply.sh
+++ b/dnsapi/dns_simply.sh
@@ -6,9 +6,11 @@
 #SIMPLY_ApiKey="apikey"
 #
 #SIMPLY_Api="https://api.simply.com/1/[ACCOUNTNAME]/[APIKEY]"
-
 SIMPLY_Api_Default="https://api.simply.com/1"
 
+#This is used for determining success of REST call
+SIMPLY_SUCCESS_CODE='"status": 200'
+
 ########  Public functions #####################
 #Usage: add  _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 dns_simply_add() {
@@ -171,7 +173,7 @@ _get_root() {
       return 1
     fi
 
-    if _contains "$response" '"code":"NOT_FOUND"'; then
+    if ! _contains "$response" "$SIMPLY_SUCCESS_CODE"; then
       _debug "$h not found"
     else
       _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
@@ -196,6 +198,12 @@ _simply_add_record() {
     return 1
   fi
 
+  if ! _contains "$response" "$SIMPLY_SUCCESS_CODE"; then
+    _err "Call to API not sucessfull, see below message for more details"
+    _err "$response"
+    return 1
+  fi
+
   return 0
 }
 
@@ -211,6 +219,12 @@ _simply_delete_record() {
     return 1
   fi
 
+  if ! _contains "$response" "$SIMPLY_SUCCESS_CODE"; then
+    _err "Call to API not sucessfull, see below message for more details"
+    _err "$response"
+    return 1
+  fi
+
   return 0
 }