Secure Deep Linking to Claim Center

Overview

To provide a more seamless claim experience for your customers, Cermati Protect allows partners to deep-link directly into our Claim Center. Customers will visit our Claim Center via a URL with encrypted Coverage ID appended in it.

By appending the customer's Coverage ID to the URL parameters, the Claim Center will automatically prefill the Coverage ID field. This would reduce manual typing errors for the customer.

Endpoint Structure

Partners should redirect users to our Claim Center using the following URL format:

GET https://[claim-center-base-url]/cermatiprotect/claim?plcy=<base64url_payload>&ts=<base64url_timestamp>&id=<id>

Query Parameters

ParameterTypeRequiredDescription
plcyStringRequiredThe Base64URL-encoded string containing the encrypted Coverage ID payload and its initialization vector (IV).
tsStringRequiredThe Base64URL-encoded string containing the encrypted timestamp payload with format 2026-12-31T23:59:59.999Z (ISO 8601) and its IV. The timestamp is useful to make the payload temporary. The payload will be invalid when 1 hour has passed since the given timestamp
idStringRequiredthe id provided securely to the partner from us, in plain text

Cryptographic Specifications

To successfully generate the parameters, your system must implement the following encryption scheme:

Key Derivation (PBKDF2)

Before encrypting payloads, derive a 256-bit key from the password provided to you during onboarding.

ParameterValue / ConfigurationNotes
Password[Provided Securely]The shared secret provided during onboarding
SaltThe first 6 characters of the PasswordDerived dynamically from the password string.
Iterations600,000Recommended iterations for PBKDF2-HMAC-SHA256
HMAC AlgorithmSHA256Hashing algorithm used for key derivation.
Derived Key Length32 bytes (256 bits)The resulting key. Use directly as the key for AES-256-CBC encryption

Encryption (AES-256-CBC)

For every encryption operation (both Coverage ID and timestamp):
a. Apply PKCS7 padding to the plaintext payload to match block size requirements.
b. Generate a cryptographically secure, random 16-byte Initialization Vector (IV). Never reuse an IV across different operations.
c. Derive the key using the password and salt. Use 600,000 iterations, SHA256, and length of 32 bytes.
d. Encrypt the padded plaintext with the derived key and generated IV using the AES-256-CBC.
e. Convert the resulting ciphertext and the IV into lowercase Hex strings, then concatenate them with a dot delimiter:

ciphertextHex.ivHex

f. Encode the entire concatenated string using Base64URL. Do not use standard Base64; ensure + is replaced by -, / by _, and trailing padding = is stripped.

Code Example (Node.js)

const crypto = require('crypto');
const Bluebird = require('bluebird');

const cryptoRandomBytesAsync = Bluebird.promisify(crypto.randomBytes, { context: crypto });

// Configuration (Ensure these match your credentials safely stored in environment variables)
const SHARED_SECRET = 'your_16_char_secret'; 
const ALGORITHM = 'aes-256-cbc';
const ITERATIONS = 600000;
const KEY_LENGTH = 32;
const DIGEST = 'sha256';
const ID = 'your_given_id';

/**
 * Encrypts a plaintext string into a Base64URL-encoded payload
 */
const encryptPayload = async (plaintext) => {
  const passwordBuf = Buffer.from(SHARED_SECRET, 'utf8');
  const saltBuf = Buffer.from(SHARED_SECRET.slice(0, 6), 'utf8');
  
  // 1. Derive Key
  const keyBuf = crypto.pbkdf2Sync(passwordBuf, saltBuf, ITERATIONS, KEY_LENGTH, DIGEST);
  
  // 2. Generate random IV
  const ivBuf = await cryptoRandomBytesAsync(16);
  const ivHex = ivBuf.toString('hex');
  
  // 3. Encrypt data
  const cipher = crypto.createCipheriv(ALGORITHM, keyBuf, ivBuf);
  let ciphertextHex = cipher.update(plaintext, 'utf8', 'hex');
  ciphertextHex += cipher.final('hex');
  
  // 4. Serialize and Base64URL encode
  const serializedPayload = `${ciphertextHex}.${ivHex}`;
  const base64UrlPayload = Buffer.from(serializedPayload, 'utf8').toString('base64url');
  
  return base64UrlPayload;
};

(async () => {
  try {
    const coverageId = 'CP-12345-67890';
    const currentTimestamp = new Date().toISOString();

    const plcyParam = await encryptPayload(coverageId);
    const tsParam = await encryptPayload(currentTimestamp);

    const redirectUrl = `https://claim.cermatiprotect.com/cermatiprotect/claim?plcy=${plcyParam}&ts=${tsParam}&id=${ID}`;
    console.log('Constructed Redirection URL:\n', redirectUrl);
  } catch (error) {
    console.error('Encryption failed:', error);
  }
})();