Skip to main content

Soft identification

Soft identification, sometimes called "soft login", allows identification of a contact, for example over the web or by an e-com, who has arrived via an email link. When you are configured for soft identification, an encrypted JSON is sent in the link's query string, containing the information needed to identify the contact.

This is what the data can look like in the query string:


The data contains the information needed to identify the contact and present a personalized web experience. When decrypted it looks something like this:

"contactid": "1c9b35d6-28d8-4520-8900-a89555f7f0c5",   
"email": "",
"dateTime": "2018-07-11T11:19:54Z",
"discoveryKey" : "6iub35d6-34d8-6666-8900-e89100f7f0c5"
  • contactId: Unique ID for the contact in Engage, used for subsequent API-calls to get all relevant information about a contact

  • email: Contact's email address

  • id: Contact's key as defined by the project

  • dateTime: Specifies the time and date the link was generated (V.5)

  • discoveryKey: Contact's identifier for Voyado Elevate (optional)


To include the discoveryKey from Voyado Elevate, ask your Voyado team to set it up for you (which they will do using email.autologin.includeDiscoveryKey).


Be careful about the access a user is granted by a soft login. A soft login link is a convenient way of identification but it can also be easily shared, for example by a user forwarding an email containing the link to a friend.

Decoding the message

  1. Extract the value string from the parameter “eClub”

  2. Decode the string from URL-safe Base64:

    • Replace all “-” with “+”

    • Replace all “_” with “/”

    • Add padding characters, based on the length

    • Decode the string using normal Base64 decoding

  3. The initialization vector is stored in the first 16 bytes; the remainder is the encoded message

  4. Decrypt the message using AES-256 (CBC) using the shared key and the initialization vector from step 3

Encoding the message

  1. The data is sent as JSON following the example above.

  2. The JSON string is encrypted using AES-256 with a 32 byte shared key (exchanged during implementation) and a unique initialization vector for each message.

  3. The initialization vector and the encrypted string are concatenated, Base64 encoded and modified for URL inclusion (URL-safe Base64).

  4. The complete encoded string is then sent as the query-string parameter “eClub”.

C# decryption code example

public static string Decrypt(string keyString, string stringToDecrypt)
    var key = Encoding.UTF8.GetBytes(keyString);
    stringToDecrypt = stringToDecrypt.Replace('-', '+').Replace('_', '/');
    switch (stringToDecrypt.Length % 4)
        case 2: stringToDecrypt += "=="; break;
        case 3: stringToDecrypt += "="; break;
    var bytes = Convert.FromBase64String(stringToDecrypt);
    var iv = new byte[16];
    var text = new byte[bytes.Length - 16];
    Array.Copy(bytes, iv, 16);
    Array.Copy(bytes, 16, text, 0, text.Length);
    string plaintext = null;
    using (var aes = Aes.Create())
        aes.Padding = PaddingMode.PKCS7;
        aes.IV = iv;
        aes.Key = key;
        aes.Mode = CipherMode.CBC;
        var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
        using (var msDecrypt = new MemoryStream(text))
            using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                using (var srDecrypt = new StreamReader(csDecrypt))
                    plaintext = srDecrypt.ReadToEnd();

    return plaintext;

Perl decryption code example

use Crypt::Mode::CBC;
use MIME::Base64::URLSafe;

my $key = 'THE SHARED KEY';
my $input = 'THE ENCRYPTED VALUE';

$input = urlsafe_b64decode($input);
my $iv = substr($input, 0, 16);
my $data = substr($input, 16);
my $cbc = Crypt::Mode::CBC->new('AES');
my $decrypted = $cbc->decrypt($data, $key, $iv);

PHP decryption code example

function decrypt($key, $encrypted)
    $encrypted = base64_decode(strtr( $encrypted, '-_', '+/') . str_repeat('=', 3 - ( 3 + strlen( $encrypted )) % 4 ));
    $iv = substr($encrypted, 0, 16);
    $encrypted = substr($encrypted, 16);
    $decrypted = openssl_decrypt($encrypted,'aes-256-cbc',$key,OPENSSL_RAW_DATA, $iv);
    return $decrypted;

node.js code example

const crypto = require('crypto');

const stringToDecrypt = "INSERT STRING TO DECRYPT HERE";
const keyString = "INSERT KEY HERE";

const key = Buffer.from(keyString, 'utf8');
let stringToDecryptFixed = stringToDecrypt.replace('-', '+').replace('_', '/');
switch (stringToDecryptFixed.length % 4) {
    case 2: stringToDecryptFixed += "=="; break;
    case 3: stringToDecryptFixed += "="; break;
const bytes = Buffer.from(stringToDecryptFixed, 'base64');
const iv = Buffer.alloc(16);
const text = bytes.slice(16);
bytes.copy(iv, 0, 0, 16);

let plaintext = null;
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
plaintext = Buffer.concat([decipher.update(text),]).toString('utf-8');