Skip to main content

Signed Links

Harlyy supports signed survey links using the sig query parameter, similar to how other query parameters can be used to create more dynamic behaviour. Signed links allow you to verify that a survey link was generated by your system, has not been tampered with and only gets used once.

This is commonly used by POS integrations and other backend systems that need to securely generate one-time, customer-specific survey links.

A signed link contains:

  1. A Base64-encoded JSON payload
  2. An HMAC-SHA256 signature generated using your survey secret

The format is:

?sig=<base64_payload>.<signature>

For example:

https://survey.harlyy.com/<business_id>/<survey_id>?sig=<payload>.<signature>

The payload is signed using the survey's secret key. Harlyy validates the signature before allowing the survey submission flow to continue.

Payload Structure

The payload must be a JSON object encoded as Base64.

Supported fields

FieldTypeRequiredDescription
noncestringYesA unique identifier for the generated link.
expnumberNoExpiration timestamp in milliseconds since Unix epoch.

Example payload before encoding:

{
"nonce": "abc123",
"exp": 1789500000000
}

Survey Secret and Signature Settings

To manage your secret and settings, navigate to the survey builder, then click "Settings". Underneath "Additional Settings" you can view or regenerate your survey's signing secret, and you can enable the "require survey link signing" option.

Survey Settings

Example Code

The following examples demonstrate how to generate a signed HMAC-SHA256 link:

NodeJS Example
import crypto from 'crypto';

function createSignedPayload(nonce, secret, expiresAt) {
const payloadObj = {
nonce,
};

if (expiresAt instanceof Date) {
payloadObj.exp = expiresAt.getTime();
}

// Convert JSON -> string
const json = JSON.stringify(payloadObj);

// Convert to Base64
const payloadBase64 = Buffer.from(json, 'utf8').toString('base64');

// Create HMAC SHA256 signature
const signature = crypto
.createHmac('sha256', secret)
.update(payloadBase64, 'utf8')
.digest('base64');

return encodeURIComponent(`${payloadBase64}.${signature}`);
}

// Example usage
const sig = createSignedPayload(
'abc',
'my-super-secret-key',
Date.now() + 30 * 60 * 60 * 1000
);

console.log(sig);

Security Recommendations

warning
  • Never expose your survey secret in frontend applications or mobile apps.
  • Signed links should always be generated server-side.
  • Use a unique nonce value for every generated link.
  • Use expiration timestamps (exp) whenever possible, with an appropriate expiry time.