/Data privacy & encryption

Data privacy & encryption

Forward responses without storing them, and end-to-end encrypt so no one β€” not even Halloform β€” can read them.

Two privacy modes#

Both are opt-in per form and live under Settings β†’ Behavior. They can be combined for maximum privacy.

  • Don't store responses β€” responses are forwarded to your connected destinations and never kept on Halloform.
  • Encrypt responses (end-to-end) β€” responses are locked in the respondent's browser with your public key; only you can read them, and Halloform never sees the plaintext.

Don't store responses#

When on, a completed response is sent straight to your connected destinations (Google Sheets, Notion, a webhook, Slack…) and is then purged β€” nothing is kept in Halloform's database. Because there's no stored copy, these responses won't appear in Results; they live only in your own tools.

Connect at least one destination in the Integrations tab first. With this on and no destination, responses have nowhere to go and are lost.

End-to-end encryption#

Encryption uses public-key cryptography (a libsodium sealed box). You hold a keypair: the PUBLIC key is stored on the form and used to encrypt; the PRIVATE key is yours alone and decrypts. The respondent's browser encrypts every answer before it leaves the page, so Halloform's servers only ever receive ciphertext β€” we can't read your responses, and neither can anyone who breaches us.

  • Encrypted responses can only be sent to a webhook β€” a Sheet, Notion, or email would just hold unreadable ciphertext.
  • You (or your developer) decrypt the ciphertext on your own server with your private key.
  • It pairs with Don't store: nothing readable is ever kept on Halloform.
Lose the private key = data unrecoverable
Halloform never stores your private key and CANNOT recover it. If you lose it, every response encrypted to it is permanently unreadable. Store it in a password manager or secrets vault.

Enable encryption#

  1. Settings β†’ Behavior β†’ Encrypt responses β†’ Enable encryption.
  2. Generate & download key (recommended): a keypair is created in your browser, your private key downloads as a file (save it!), and the public key is stored on the form. Or choose Advanced to paste a public key you generated yourself offline.
  3. Turn on Don't store responses and connect a webhook in Integrations β€” encrypted responses are webhook-only.

Decrypt on your webhook#

Your webhook receives a JSON body with an `encrypted` field (base64 ciphertext) instead of readable answers. Decrypt it with libsodium and your private key:

import sodium from 'libsodium-wrappers';
await sodium.ready;

const PRIVATE_KEY = process.env.FORM_PRIVATE_KEY; // base64, keep secret
const PUBLIC_KEY  = process.env.FORM_PUBLIC_KEY;  // base64

function decrypt(encryptedB64) {
  const cipher = sodium.from_base64(encryptedB64, sodium.base64_variants.ORIGINAL);
  const pk = sodium.from_base64(PUBLIC_KEY,  sodium.base64_variants.ORIGINAL);
  const sk = sodium.from_base64(PRIVATE_KEY, sodium.base64_variants.ORIGINAL);
  const opened = sodium.crypto_box_seal_open(cipher, pk, sk);
  return JSON.parse(sodium.to_string(opened)); // β†’ the answers object
}

// app.post('/webhook', (req, res) => {
//   const answers = decrypt(req.body.encrypted);
//   res.sendStatus(200);
// });
It's standard libsodium (crypto_box_seal_open), so any language's libsodium binding works β€” Python, Go, Rust, PHP, etc., not just Node.

Important notes#

  • Encryption is a developer feature β€” decrypting requires running the snippet above on your server.
  • Encrypted forms don't work with features that need to read answers server-side: analytics, search, Sheets/Notion, quiz scoring, PDF fill. Use plain fields + a webhook.
  • Rotating the public key only affects new responses; older ones stay encrypted to the previous key.
  • The private key is never sent to Halloform in either the generate or paste flow.