Webhooks Integration Guide

To enable webhooks to be sent from your Scorecard go to Integrate > Webhooks and click Connect to add the URL that you'd like to receive webhooks too.

Note: You must provide a secure (https) URL that can receive POST requests

Once you have provided your secure URL to receive post requests you will be able to specify a secret key and which events you'd like to receive.

Events

There are 4 possible events you may receive as a webook:


Quiz Started (QUIZ_STARTED)

This webhook is sent when somebody answers the first question in your quiz. If your lead form is configured to show before the quiz questions, the lead form data such as name/email will included in the webhook payload.

Quiz Finished (QUIZ_FINISHED)

This webhook is sent when somebody answers the last question in your quiz. If your lead form is configured to show before the quiz questions, the lead form data such as name/email will included in the webhook payload however if it's configured to show at the end of the quiz questions this webhook will be sent prior to the lead form being shown/completed.

Lead Details Updated (LEAD_DETAILS_UPDATED)

This webhook is sent if a lead subsequently changes their sign up details after receiving their results. Usually via a form on your result page.

Lead Signed Up (LEAD_SIGNED_UP)

This webhook is sent when a lead provides their details (normally name and email) via the lead form. If your lead form is configured to show before the quiz questions this will be sent before any quiz questions are answered and will therefore the request payload will only include the lead form data. If the lead form is set after the quiz questions, all quiz answers and data will be available in the request payload.

Webhook Payload Format


General structure

{
"event_name" : "Event name",
"data": {} // - Response data
}

Example QUIZ_STARTED webhook

{
  "event_name" : "QUIZ_STARTED",
  "data"       : {
      "id": "1e11c712-c4f7-48e9-8bc7-7d86ea273a6f",
      "status": "Started",
      "key": "64b00a39ce128979457258",
      "first_name": null,
      "last_name": null,
      "email": "example@example.com",
      "started_at": "2023-07-06T08:12:09.000Z",
      "opt_in": true,
      "opt_in_detail": "Implied",
      "utm_source": null,
      "utm_campaign": null,
      "utm_medium": null,
      "utm_term": null,
      "utm_content": null,
      "ip_address": "172.23.0.1",
      "lead_form_questions": [ //- possible if we added fields to lead form.
          {
              "id": "01a725d2-bd2c-4aa0-a799-6d6fc8e7ac9d",
              "question": "Question",
              "answers": [
                  {
                      "answer": "Answer"
                  }
              ]
          },
      ],
      "quiz_questions": [],
      "abandon_email_url": "https://example.scoreapp.com/continue/64a6775915bf6092267456"
  }
};

Example QUIZ_FINISHED webhook

{
  "event_name" : "QUIZ_FINISHED",
  "data"       : {
      "id": "1e11c712-c4f7-48e9-8bc7-7d86ea273a6f",
      "status": "Finished",
      "key": "64b00a39ce128979457258",
      "first_name": null,
      "last_name": null,
      "email": "example@example.com",
      "started_at": "2023-07-06T08:25:25.000Z",
      "opt_in": true,
      "opt_in_detail": "Implied",
      "utm_source": null,
      "utm_campaign": null,
      "utm_medium": null,
      "utm_term": null,
      "utm_content": null,
      "ip_address": "172.23.0.1",
      "lead_form_questions": [ //- possible if we added fields to lead form.
          {
              "id": "01a725d2-bd2c-4aa0-a799-6d6fc8e7ac9d",
              "question": "Question",
              "answers": [
                  {
                      "answer": "Answer"
                  }
              ]
          },
      ],
      "quiz_questions": [
          {
              "id": "0f9d99a8-1835-46d7-bb1f-86b579195797",
              "question": "Question 1",
              "answers": [
                  {
                      "answer": "No"
                  }
              ]
          },
          {
              "id": "d9cadc83-50df-442c-aeb2-a0878e3e7fb7",
              "question": "Question 2",
              "answers": [
                  {
                      "answer": "5"
                  }
              ]
          },
          {
              "id": "ea795cc9-e437-4734-a85a-34837dccbb2d",
              "question": "Question 3",
              "answers": [
                  {
                      "answer": "Answer 1"
                  },
                  {
                      "answer": "Answer 2"
                  },
                  {
                      "answer": "Answer 3"
                  }
              ]
          },
      ],
      "finished_at": "2023-07-06T08:25:29.000Z",
      "report": "https://example.scoreapp.com/results/64a67a753fbb6186785302/pdf",
      "lowest_category": {
          "id": "433cad86-ce58-4755-9b97-16cbbea42b15",
          "title": "Category 2"
      },
      "highest_category": {
          "id": "3fc9bef7-c890-4ec7-9414-f13929ac4e38",
          "title": "Category 1"
      },
      "total_score": {
          "actual": "67.00",
          "percent": 54,
          "denominator10": 5.36,
          "tier": "medium"
      },
      "category_scores": [
          {
              "actual": "56.00",
              "percent": 50,
              "denominator10": 4.96,
              "tier": "medium",
              "category": {
                  "id": "e6b19afd-891c-41cc-ab44-c7e2e8485acd",
                  "title": "Category 1"
              }
          },
          {
              "actual": "4.00",
              "percent": 33,
              "denominator10": 3.33,
              "tier": "low",
              "category": {
                  "id": "ff35527a-2cb8-4ed0-b142-5bad8ac14818",
                  "title": "Category 2"
              }
          },
      ],
      "result_url": "https://example.scoreapp.com/results/64a67a753fbb6186785302",
      "audiences": [
          {
              "id": "30672ea7-8515-46bd-bde1-09b8e0986873",
              "name": "Audience 1"
          },
          {
              "id": "278e64c9-71e4-4cb1-bc70-009e33357336",
              "name": "Audience 2"
          }
      }
};

Example LEAD_SIGNED_UP webhook

{
  "event_name" : "LEAD_SIGNED_UP",
  "data"       : {
      "id": "1e11c712-c4f7-48e9-8bc7-7d86ea273a6f",
      "status": "Started",
      "key": "64b00a39ce128979457258",
      "first_name": null,
      "last_name": null,
      "email": "example@example.com",
      "started_at": "2023-07-06T08:12:09.000Z",
      "opt_in": true,
      "opt_in_detail": "Implied",
      "utm_source": null,
      "utm_campaign": null,
      "utm_medium": null,
      "utm_term": null,
      "utm_content": null,
      "ip_address": "172.23.0.1",
      "lead_form_questions": [ //- possible if we added fields to lead form.
          {
              "id": "01a725d2-bd2c-4aa0-a799-6d6fc8e7ac9d",
              "question": "Question",
              "answers": [
                  {
                      "answer": "Answer"
                  }
              ]
          },
      ],
      "quiz_questions": [],
      "abandon_email_url": "https://example.scoreapp.com/continue/64a6775915bf6092267456"
  }
};

Example LEAD_DETAILS_UPDATED webhook

{
  "event_name" : "LEAD_DETAILS_UPDATED",
  "data"       : {
      "id": "1e11c712-c4f7-48e9-8bc7-7d86ea273a6f",
      "status": "Finished",
      "key": "64b00a39ce128979457258",
      "first_name": null,
      "last_name": null,
      "email": "example.changed@example.com",
      "started_at": "2023-07-06T08:25:25.000Z",
      "opt_in": true,
      "opt_in_detail": "Implied",
      "utm_source": null,
      "utm_campaign": null,
      "utm_medium": null,
      "utm_term": null,
      "utm_content": null,
      "ip_address": "172.23.0.1",
      "lead_form_questions": [ //- possible if we added fields to lead form.
          {
              "id": "01a725d2-bd2c-4aa0-a799-6d6fc8e7ac9d",
              "question": "Question",
              "answers": [
                  {
                      "answer": "Answer changed"
                  }
              ]
          },
      ],
      "quiz_questions": [
          {
              "id": "0f9d99a8-1835-46d7-bb1f-86b579195797",
              "question": "Question 1",
              "answers": [
                  {
                      "answer": "No"
                  }
              ]
          },
          {
              "id": "d9cadc83-50df-442c-aeb2-a0878e3e7fb7",
              "question": "Question 2",
              "answers": [
                  {
                      "answer": "5"
                  }
              ]
          },
          {
              "id": "ea795cc9-e437-4734-a85a-34837dccbb2d",
              "question": "Question 3",
              "answers": [
                  {
                      "answer": "Answer 1"
                  },
                  {
                      "answer": "Answer 2"
                  },
                  {
                      "answer": "Answer 3"
                  }
              ]
          },
      ],
      "finished_at": "2023-07-06T08:25:29.000Z",
      "report": "https://example.scoreapp.com/results/64a67a753fbb6186785302/pdf",
      "lowest_category": {
          "id": "433cad86-ce58-4755-9b97-16cbbea42b15",
          "title": "Category 2"
      },
      "highest_category": {
          "id": "3fc9bef7-c890-4ec7-9414-f13929ac4e38",
          "title": "Category 1"
      },
      "total_score": {
          "actual": "67.00",
          "percent": 54,
          "denominator10": 5.36,
          "tier": "medium"
      },
      "category_scores": [
          {
              "actual": "56.00",
              "percent": 50,
              "denominator10": 4.96,
              "tier": "medium",
              "category": {
                  "id": "e6b19afd-891c-41cc-ab44-c7e2e8485acd",
                  "title": "Category 1"
              }
          },
          {
              "actual": "4.00",
              "percent": 33,
              "denominator10": 3.33,
              "tier": "low",
              "category": {
                  "id": "ff35527a-2cb8-4ed0-b142-5bad8ac14818",
                  "title": "Category 2"
              }
          },
      ],
      "result_url": "https://example.scoreapp.com/results/64a67a753fbb6186785302",
      "audiences": [
          {
              "id": "30672ea7-8515-46bd-bde1-09b8e0986873",
              "name": "Audience 1"
          },
          {
              "id": "278e64c9-71e4-4cb1-bc70-009e33357336",
              "name": "Audience 2"
          }
      }
};

Failures and Retries

If the URL to which we send a webhook fails to send a response with a 2xx status code we will consider the call as failed. The call will also be considered failed if the remote app doesn't respond within 10 seconds

If the call fails we will attempt to resend the webhook up to a maximum of 5 times.


Optional Webhook Header / Secret Key (Recommended)

If you provide an optional “WEBHOOK SECRET KEY”, a header (Scoreapp-Signature ) will be included with each webhook request containing a hashed string of the request data using the secret key as the signing secret allowing you to securely validate the authenticity of the request.

To validate the request you will need to recreate the hashed string using the same secret key and compare the one sent in the Scoreapp-Signature header with the one you recreate. If they match then the request is valid and you can continue processing the webhook data


The following are examples of handling the Scoreapp-Signature header in various coding languages and frameworks.


PHP

public function webhookHandler(Request $request)
{
      $requestContent = file_get_contents('php://input');
      $signature = isset($_SERVER['HTTP_SCOREAPP_SIGNATURE']) ? $_SERVER['HTTP_SCOREAPP_SIGNATURE'] : '';

      $signingSecret = 'Your webhook secret key';
      $computedSignature = hash_hmac('sha256', $requestContent, $signingSecret);

      if (!hash_equals($signature, $computedSignature)) {
            header('HTTP/1.1 401 Unauthorized');
            echo 'Unauthorized';
            exit;
      }

      // your code goes here

      header('HTTP/1.1 200 OK');
      echo 'Webhook handled';
}

Python (using Flask)

from flask import Flask, request, abort
import hashlib
import hmac

app = Flask(__name__)

@app.route('/webhook', methods=['POST'])
def webhook_handler():
    signature = request.headers.get('Scoreapp-Signature')
    signing_secret = 'Your webhook secret key'
    
    computed_signature = hmac.new(signing_secret.encode(), request.data, hashlib.sha256).hexdigest()
    
    if not hmac.compare_digest(signature, computed_signature):
        abort(401)
    
    # your code goes here
    
    return 'Webhook handled', 200

if __name__ == '__main__':
    app.run(debug=True)

Node.js (using Express)

const express = require('express');
const crypto = require('crypto');
const app = express();

app.use(express.json());

app.post('/webhook', (req, res) => {
    const signature = req.headers['scoreapp-signature'];
    const signingSecret = 'Your webhook secret key';
    
    const computedSignature = crypto
        .createHmac('sha256', signingSecret)
        .update(JSON.stringify(req.body))
        .digest('hex');
    
    if (computedSignature !== signature) {
        return res.status(401).send('Unauthorized');
    }
    
    // your code goes here
    
    res.status(200).send('Webhook handled');
});

app.listen(3000, () => {
    console.log('Server is running on port 3000');
});

Java (using Spark Java)

import static spark.Spark.*;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

public class WebhookHandler {
    public static void main(String[] args) {
        post("/webhook", (request, response) -> {
            String signature = request.headers("Scoreapp-Signature");
            String signingSecret = "Your webhook secret key";
            String requestBody = request.body();

            Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
            SecretKeySpec secret_key = new SecretKeySpec(signingSecret.getBytes(), "HmacSHA256");
            sha256_HMAC.init(secret_key);
            String computedSignature = Base64.getEncoder().encodeToString(sha256_HMAC.doFinal(requestBody.getBytes()));

            if (!computedSignature.equals(signature)) {
                response.status(401);
                return "Unauthorized";
            }

            // your code goes here

            response.status(200);
            return "Webhook handled";
        });
    }
}

Still need help? Contact Us Contact Us