LogoLogo
Back to studio
  • Formsort documentation
  • ⏩Quickstart
    • 🟒Get started with Formsort
    • 🍎Core concepts
    • ℹ️Question and content reference
    • πŸ“•Key terms
    • 🏞️Create your first flow
    • πŸ“–Add content and collect answers
      • 🀳Capture demographic data
      • ℹ️Add informational content
      • πŸ” Review your variable schema
    • 🎨Create a theme
      • Set brand guidelines
    • 🀹Personalize your flow
      • 🌟Template your variables
      • 🧠Add conditional logic
      • πŸ’«Using conditional logic with Calculated and API variables
      • πŸ”šEnd the flow
    • πŸ”€Set up integrations
    • πŸš€Go live
      • Auditing your flow for content, functionality, and design
    • πŸ’ΌCommon use cases
      • πŸ’”Disqualify responders
      • πŸ—“οΈAdd a scheduling option
      • πŸ“„Allow responders to read and accept your company policies
  • πŸ—οΈBuilding flows
    • Flows and variants
      • Flow starts
    • Adding content
      • Groups
      • Steps
        • Settings
        • Logic
        • Style
      • Questions
        • General Settings
        • Style
        • Address
        • Comparison
        • Confirmation
        • Date
        • Date & Time
        • Email address
        • File upload
        • Grid choice
        • Iframe
        • Image upload
        • Number
        • Payment
        • Phone number
        • Postal code
        • Question group
        • Region
        • Select
          • Providing choices via API or calculation
        • Signature
        • SSN
        • Text questions
        • Yes/No
      • Content
        • General Settings
        • Statement
        • Image
        • Video
        • Next button
        • Divider
        • Map
      • Endings
      • Using markdown
      • Using variable templating
        • Template formatting functions
      • Copy-pasting form content
      • Content library
    • Conditions and logic
      • Logical operator reference
      • Advanced logic
    • Variables (answers)
      • Variables from questions
      • Externally provided variables
      • Calculated variables
      • API lookups
      • System Library variables
      • Orphaned variables
    • Schemas
      • JSON Schemas
      • Validating flow schemas
    • Redirects
    • Styling and themes
      • CSS Reference
      • Overriding theme styling
      • Custom CSS overrides
      • Content area
      • Animations and transitions
      • Form Layout
      • Typography
        • Adobe Fonts
        • Hosting custom fonts
      • Color variables
      • Dimension variables
      • Question containers
      • Assets
      • Form Buttons
        • Select buttons
      • Inputs and dropdowns
      • Checkmarks
      • Tables
      • Sliders
      • Divider lines
      • Progress bar
      • Comparison cards
    • Variant settings
      • Form behavior for returning responders
      • Group ranking API
    • Publishing and versions
      • Preview window
      • Deploying
      • History
  • πŸ’ΎHandling data
    • Philosophy and data retention policy
    • Viewing form answers
    • Responder UUIDs
    • Environments
      • Loading different environments
    • Passing data in
      • URL parameters
      • POST body
      • Embed query parameters
    • Custom validators
    • Form answers and events
      • Analytics events
      • Signed requests
      • Event payload shape
      • Submission frequencies
      • Runtime error reporting
      • Admin API
      • Flow content data format
    • Integration reference
      • Amplitude
        • Amplitude cross domain tracking
      • BigQuery
      • FullStory
      • Google Analytics
        • Updating from Universal Analytics to GA4
      • Google Cloud Storage
      • Google Sheets
      • Google Tag Manager (GTM)
        • JavaScript triggered by flow events
      • Hubspot
      • Jornaya
      • Optimizely
      • PostgreSQL
      • Redshift
      • Rudderstack
      • S3
      • Salesforce
      • Segment
        • Segment Setup
        • Segment cross domain tracking
      • Stripe
      • TrustedForm
      • Webhooks
        • Zapier
  • πŸ“ΊGoing live
    • Custom domains
    • Built-in analytics
    • Embedding
      • Web-Embed API
        • React-embed
      • Adding authentication
      • Embedding forms in iOS and Android
      • Setting up a dev environment
    • Split testing
    • Preflight checklist
  • 🏒Teams
    • Accounts
      • Roles and permissions
    • Events subscriptions
    • Workspace domain detection
Powered by GitBook
On this page
  • Signing requests
  • How to verify a signature

Was this helpful?

  1. Handling data
  2. Form answers and events

Signed requests

Sign events and webhooks

PreviousAnalytics eventsNextEvent payload shape

Last updated 11 months ago

Was this helpful?

Signing requests

Both webhooks and events can be signed for extra security.

Signing webhooks

For webhooks if you'd like the requests to be signed to ensure that they are authentic requests from formsort, you can enable the sign security. Read more about creating a webhook .

Signing events

All events for variant_revision_published are signed by default. The signature for verfication can be found on the "Events" page.

Note: The signing key is generated by Formsort, and is different across events and webhooks.

How to verify a signature

To generate the signature for verification, use the original HTTP request body (aka JSON payload) and:

  1. Hash the request body with SH256, encrypting it with signing key.

    1. You can obtain the key in the Integrations tab when this option is enabled.

  2. Base64 encode the result in a URL-safe-way, using - and _ instead of the + and / characters. This is necessary as Formsort sends the signature in an HTTP header.

  3. Remove the trailing = signs. Those are often generated by the hashing libraries as padding.

Sample implementations of the signature code follow:

import base64
import hashlib
import hmac


def as_bytes(v):
    return v.encode("utf8")


def hmac_sign(signing_key, original_request_body):  # unmodified, a jsonified string
    key = as_bytes(signing_key)
    message = as_bytes(original_request_body)
    return (
        # Note:
        # 1. for url safety, use - and _ characters instead of + and / respectively
        # 2. remove the padding = signs at the end of the signature
        base64.urlsafe_b64encode(
            hmac.new(key, message, hashlib.sha256).digest())
            .rstrip(b"=")
            .decode("utf8")
    )

To verify a signature in Javascript, the original request buffer can be verified with the signing key like follows:

import crypto from 'crypto';

const hmacSign = (signingKey, originalRequestBuffer) => {
    const key = Buffer.from(signingKey, 'utf8');
    return (
        crypto
            .createHmac('sha256', key)
            .update(originalRequestBuffer)
            .digest('base64')
            .replace(/\+/g, '-')
            .replace(/\//g, '_')
            .replace(/=+$/, '')
    );
}

If you are using express as your webserver, and have enabled JSON body parsing with express.json() be careful that you are signing the original body buffer and not the parsed JSON. Signing a stringified req.body is not reliable due to differences in whitespace between implementations and settings.

The simplest way to do this is to ensure that routes for receiving Formsort webhooks do not apply the JSON middleware.

Using the hmacSign function above, assuming a SIGNING_KEY environment variable, the following will sign requests from Formsort:

import crypto from 'crypto';
import express from 'express';
import bodyParser from 'body-parser';
const PORT = 8080;

const app = express();

app.use((req, res, next) => {
  if (req.header('x-formsort-secure') === 'sign') {
    // Signed formsort requests require verification of the raw body content
    bodyParser.raw({ type: 'application/json' })(req, res, next);
  } else {
    express.json()(req, res, next);
  }
});

const hmacSign = (signingKey, originalRequestBuffer) => {
    const key = Buffer.from(signingKey, 'utf8');
    return (
        crypto
            .createHmac('sha256', key)
            .update(originalRequestBuffer)
            .digest('base64')
            .replace(/\+/g, '-')
            .replace(/\//g, '_')
            .replace(/=+$/, '')
    );
}

app.post('/formsort-webhook', (req, res) => {
  const signature = hmacSign(process.env.SIGNING_KEY, req.body);
  if (signature !== req.header('x-formsort-signature')) {
    return res.status(401).json({ message: 'Formsort signature mismatch' });
  }

  const answers = JSON.parse(req.body);
  return res.json({
    message: 'Well now, it looks like you are all good.',
    answers,
  });
});

app.listen(PORT, () => {
  console.log(`listening on port ${PORT}`);
});

Note

When signing is enabled, Formsort will send the following additional HTTP headers on webhook requestsο»ΏIf signature is enabled, then X-Formsort-Secure will have the value of "sign" and X-Formsort-Signature will have the signature itself.

Header
Value

X-Formsort-Secure

sign

X-Formsort-Signature

{the hash}

πŸ’Ύ
here
Click "Show signing key"