Webhooks

What are Webhooks?

A webhook is an event or a notification that's being sent from one location to the other when an action occurs. For QuestionScout, the event that will trigger the webhook is a submission in one of our forms. When a submission is stored, a notification containing the response data is being sent straight to your chosen destination - the webhook URL which you configure in the form settings.

Webhook notifications are sent via HTTP POST request, and the request body is in JSON format.

This is an example of what the request body in JSON looks like:

{
  "formId": "11a111aaaaaa1aaa111a1aa1ca",
  "eventType": "newSubmission",
  "submittedAt": "2021-06-03T10:46:44.887Z",
  "completionTime": 15,
  "number": 12,
  "ip": "00.000.000.000",
  "submissionId": "11a111aaaaaa1aaa111a1aa1ca",
  "urlParams": {},
  "calculationVariables": {
    "Score": 0
  },
  "formName": "Demo form",
  "fields": [
    {
      "name": "Dropdown",
      "_id": "11a111aaaaaa1aaa111a1aa1cb",
      "type": "dropdown",
      "visible": true,
      "section": "root",
      "position": 3,
      "childrens": [],
      "value": "CH"
    },
    {
      "name": "Short Answer",
      "_id": "11a111aaaaaa1aaa111a1aa1cc",
      "type": "shortText",
      "visible": true,
      "section": "root",
      "position": 4,
      "childrens": [],
      "value": "33"
    },
  ],
  "sections": []
}

How to set up Webhooks

To set up Webhooks on your form, you need to open the form you would like to enable Webhooks on, and go to the Settings tab. 

For this example, we use Webhook.site to see if the Webhook is coming through correctly.

On the form settings tab, you will find an area to put in your Webhooks at the bottom of the page.


Clicking "Add a Webhook" will open a model where you can enter your endpoint.

When you've entered the endpoint for the Webhook, clicking "Add Webhook" will show you the POST + Endpoint on the form settings tab.

Webhook Post Structure

Below you can find a more thorough explanation of the way our webhook post requests are structured. This is particularly helpful in the case of developing additional functionality on top of QuestionScout's Webhooks.

/* -------------------------------------------------------------------------- */
/*                              1. INTRODUCTION                               */
/* -------------------------------------------------------------------------- */

/*
    Fields and sections:
      QuestionScout forms can be made out of fields and sections.
      Sections are used as containers for fields or other sections.
      If a field or a section is not inside a section then it is
      inside a special root section. 
*/

/* -------------------------------------------------------------------------- */
/*                    2. WEBHOOK RESPONSE - JSON STRUCTURE                    */
/* -------------------------------------------------------------------------- */

const WEBBHOOK_RESPOSNSE_STRUCTURE = {
  /* ------------------------------ FORM METADATA ----------------------------- */

  // string: Unique form identifier
  formId: '60b777b6ddf4bd5a76ab7141',

  // string: Form name set in user interface
  formName: 'Your form name',

  // array<object>: This array describes sections used inside a form.
  sections: [
    {
      // string: Unique identifier of this form element
      id: '60b77808f675ba1feb4d026e',

      // string: Identifier of a parent section of this section. If 'root' then this section doesn't have a parent.
      section: 'root',

      // string: Name of this section (visible to user)
      title: 'Section',

      // array<string>: Identifiers of fields/sections inside this section
      childrens: ['60b77818f675ba1feb4d0274', '60b7782ff675ba1feb4d0276']
    }

    /* ... */
  ],

  /* --------------------------- SUBMISSION METADATA -------------------------- */

  // string: Reason why this object was send. Either 'newSubmission' or 'paymentUpdate'.
  eventType: 'newSubmission',

  // string: Unique submission identifier
  submissionId: '60b783d94347910f9f2ad391',

  // string: Form submission date
  submittedAt: '2021-06-02T13:12:57.526Z',

  // number: Amount of time it took a user to complete the form (in seconds)
  completionTime: 185,

  // number: Unique submission index (can be used for row indexing)
  number: 1,

  // string: IP from which submission was received
  ip: '0.0.0.0',

  /*
    object: Contains values of url parameters received by a form
    
    EXAMPLE:
      ?key1=value1&key2=value2 --> {"key1": "value1", "key2" > "value2"}
  */
  urlParams: {
    // string|true - value of a url parameter with a given key or true if there was no value.
    key1: 'value1',
    key2: 'value2'

    /* ... */
  },

  // object: Payment properties received from connected payment service (PayPal, Stripe, ...)
  payments: {
    /* ... */
  },

  /* --------------------------------- RESULTS -------------------------------- */

  // object: Contains final values of calculation variables values computed by the form
  calculationVariables: {
    // number: Value of a calculation variable (depends on field values and provided logic)
    variableName1: 123,
    variableName2: 123

    /* ... */
  },

  /*
    array<object>:
      This array describes the state of fields (visibility and value) when form was submitted,
      types of these fields, and layout of a form (ordering and sections).
  */
  fields: [
    // object: describes a form element's (field/section) state and properties
    {
      // string: Unique identifier of this form element
      _id: '60b777cbf675ba1feb4d0258',

      // string: Name of this form element (visible to user)
      name: 'Short Answer',

      // number:
      //  Positive integer that describes placement of this form element inside the parent section (bigger number means that it's further in the section).
      position: 1,

      // string: Identifier of a parent section of this form element. If 'root' then this section doesn't have a parent.
      section: 'root',

      // boolean: Visibility of this form element after form was completed (depends on provided logic and user answers)
      visible: false,

      /*
        string: Type of this form element.

        Possible values:
          'shortText', 'longText', 'scale', 'signature', 'dropdown', 'checkbox', 'radio', 'fileUpload',
          'datetime', 'imageChoice', 'title', 'description'
      */
      type: 'shortText',

      /*
        array<string>:
          If type is 'section' this array contains identifiers of fields/sections nested inside this section,
          for other types this array will be empty.
      */
      childrens: [],

      /*
        null|string|number|array|object:
          Value of this form element when form was submitted.
          Type of this value depends on type of this field (see section 3)
      */
      value: null
    }
  ]
};

/* -------------------------------------------------------------------------- */
/*                     3. TYPES OF FIELDS AND THEIR VALUES                    */
/* -------------------------------------------------------------------------- */

const WEBBHOOK_RESPOSNSE_FIELDS___POSSIBLE_OBJECTS = [
  /* --------------------------- SHORT ANSWER FIELD --------------------------- */

  {
    type: 'shortText',
    childrens: [],

    // string|null: String value from text input or null if input was empty.
    value: 'abcd'

    /* (other properties - see section 2) */
  },

  /* --------------------------- EMAIL ADDRESS FIELD -------------------------- */

  {
    type: 'shortText',
    childrens: [],

    // string|null: String value from text input or null if input was empty.
    value: 'mail@gmail.com'

    /* (other properties - see section 2) */
  },

  /* --------------------------- PHONE NUMBER FIELD --------------------------- */

  {
    type: 'shortText',
    childrens: [],

    // string|null: String value from text input or null if input was empty.
    value: '123456789'

    /* (other properties - see section 2) */
  },

  /* ------------------------------ NUMBER FIELD ------------------------------ */

  {
    type: 'shortText',
    childrens: [],

    // number|null: Value from text input parsed to number or null if input was empty.
    value: 34434343434

    /* (other properties - see section 2) */
  },

  /* ---------------------------- LONG ANSWER FIELD --------------------------- */

  {
    type: 'longText',
    childrens: [],

    // string|null: String value from text input or null if input was empty.
    value: '2323232'
  },

  /* ------------------------------- SCALE FIELD ------------------------------ */

  {
    type: 'scale',
    childrens: [],

    // number|null: Value in range 0 - 10 or null if nothing was selected.
    value: 1

    /* (other properties - see section 2) */
  },

  /* ----------------------------- SIGNATURE FIELD ---------------------------- */

  {
    type: 'signature',
    childrens: [],

    // string|null: Image data URI that contains signature provided by a user or null if there is no signature.
    value: 'data:image/png;base64,iVBORw0KGgoAAAANS...'

    /* (other properties - see section 2) */
  },

  /* ----------------------------- DROPDOWN FIELD ----------------------------- */

  {
    type: 'dropdown',
    childrens: [],

    // string|null: Value (or label if value isn't provided) of a selected option or null if nothing option was selected.
    value: 'DZ'

    /* (other properties - see section 2) */
  },

  /* -------------------------- MULTIPLE CHOICE FIELD ------------------------- */

  {
    type: 'checkbox',
    childrens: [],

    // array<string>: Values (or label if a value isn't provided) of zero, one or more selected options
    value: [
      'value 1',
      'Option 2',
      'value 3'

      /* ... */
    ]

    /* (other properties - see section 2) */
  },

  /* --------------------------- SINGLE CHOICE FIELD -------------------------- */

  {
    type: 'radio',
    childrens: [],

    // string: Value (or label if value isn't provided) of a selected option or null if nothing option was selected.
    value: 'Option 2'

    /* (other properties - see section 2) */
  },

  /* ---------------------------- FILE UPLOAD FIELD --------------------------- */

  {
    type: 'fileUpload',
    childrens: [],

    // array<string>:
    //  This array contains links that can be used to download zero, one or more files uploaded by a user.
    //  These links are vailid for 72 hours.
    value: [
      'https://questionscout.s3.amazonaws.com/5cdb282e5076e4...'
      /* ... */
    ]

    /* (other properties - see section 2) */
  },

  /* ----------------------------- DATETIME FIELD ----------------------------- */

  {
    type: 'datetime',
    childrens: [],

    // string|null:
    //  Date-time provided by a user or null if there was no input. Exact format depends on form settings.
    //  Some possible formats: '05.05.1234 05:55AM', '1234-05-05 21:55', '05.05.1234'
    value: '05.05.5555 05:55AM'

    /* (other properties - see section 2) */
  },

  /* --------------------------- IMAGE CHOICE FIELD --------------------------- */

  {
    type: 'imageChoice',
    childrens: [],

    // array<object>: This array contains one or more options selected by a user
    value: [
      {
        // string: Label associated with this image choice option
        text: 'label',
        // string: url to image associated with this image choice option
        url: 'https://d3djdih2k2vfi2.cloudfront.net/forms-images/60b777b6...'
      }

      /* ... */
    ]

    /* (other properties - see section 2) */
  },

  /* ------------------------------- TITLE FIELD ------------------------------ */

  {
    type: 'title',
    childrens: [],

    // string: Title and subtitle separated with '|' symbol.
    value: 'Title | subtitle'

    /* (other properties - see section 2) */
  },

  /* ---------------------------- DESCRIPTION FIELD --------------------------- */

  {
    type: 'description',
    childrens: [],

    // string: Description
    value: 'This is a Description field'

    /* (other properties - see section 2) */
  },

  /* --------------------------------- SECTION -------------------------------- */

  {
    type: 'section',
    value: null,

    // array<string>: This array contains identifiers of fields/sections nested inside this section.
    childrens: ['60b7784ef675ba1feb4d0286', '60b7784ef675ba1feb4d0288']

    /* (other properties - see section 2) */
  }
];

Still need help? Contact Us Contact Us