How to add a webhook to Fastory?

Send data towards a custom URL with the webhook feature

Why using a webhook?

 

From your Fastory account, you can send the data collected by your contests towards the URL of your choice.

You need to create this URL in the first place.

Then, you'll be able to send the data towards your servers or web service.

In which cases can I use a webhook?

  • You need to treat data on your end in order to record scores on your server, synchronize data, login users...
  • You need to trigger a script after specific bot questions (for example sending emails) or Zapier

Add-on activation

Follow these steps:

  • From your workspace, go to the add-ons page
Notion image
 
  • Click on the search bar and type webhook
Notion image
 
Informations: if you don't have access to this add-on, click on "Contact us" and fill in the form. You'll be contacted by our team (the webhook add-on can be taken in option to your plan).

Webhook settings

Once the add-on is activated:

  • Go to Experience in your workspace
  • Go to Integrations
  • Click on the button Create integration

A window appears with the setting options:

Notion image
 

Configuration

  1. Integration Title: Assign a distinctive title for easier webhook management.
  1. Endpoint (URL): Specify the target URL for the integration.
  1. Token (optional): Enter the token if required for authentication or other processes.
  1. Once these settings are in place, click on Test connection to initiate a test payload to the endpoint and validate the connection.
{
  "createdAt": "0000-00-00T00:00:00.000Z",
  "workspaceId": "1",
  "visitorId": "123",
  "story": {
    "id": "xxxxxxxx",
    "name": "webhooks"
  },
  "type": "chatbot",
  "chatbot": {
    "answers": [
      {
        "id": "xxxxxxxx",
        "value": "Answering this",
        "label":null
      }
    ],
    "question": {
      "id": "4321",
      "value": "What is your email ?",
      "attribute": "email",
      "type": "text"
    }
  }
}

Target

You can now select a target:

  • bot: Receive responses generated by a bot following a predefined question.
  • story_bot: In addition to the bot's responses, also retrieve URL parameters and game participations from the story following a predefined question.
  • workspace: Set up global settings for your workspace. At present, this only allows you to determine after which optin the data will be sent.

Payloads

There are three different payloads:

  1. games: Encompasses all participations in various games such as runner, basketball, football, quiz, and others.
  1. chatbot: Covers all responses to questions posed by the bot, including opt-ins.
  1. contest: Includes all participations in contests, whether they're of the "instant win" type or "draw".

Games Payload

List of game types:

  • basketGame
  • runnerGame
  • footballGame
  • quizGame
  • calendarQuizGame
  • swipeQuizGame
  • memoryGame
  • swipePollGame
  • swipeMusicQuizGame
  • selfieGame
{
  "createdAt": "0000-00-00T00:00:00.000Z",
  "visitorId": "xxxxxxxx",
  "story": {
    "id": "xxxxxxxx",
    "name": "My story's name",
  },
  "type": "games",
  "games": {
    "type": "basketGame",
    "score": 0,
    "timeLeft": 0.0,
    "createdAt": "0000-00-00T00:00:00.000Z",
    "brick": {
      "id": "xxxxxxxx",
      "label": "Basket1"
    }
  }
}

Selfie Game

For games of the Selfie type, we have added participation as well as vote-type participations. Example of feedback when participating:

Notion image

Example of feedback when voting:

Notion image

⚠️ For participations when a player votes, id corresponds to the ID of the vote, and vote corresponds to the ID of the participation for which the user voted.

Chatbot Payload

Here's the payload sent following the answer to an email type question:

{
  "createdAt": "0000-00-00T00:00:00.000Z",            // Payload creation date
  "workspaceId": "xxxxxxxx",                          // Fastory's workspaceID
  "visitorId": "xxxxxxxx",                            // Fastory's VisitorID
  "story": {
    "id": "xxxxxxxx",                                 // Story's ID
    "name": "My story's name",                        // Story's name
  },
  "type": "chatbot",                                  // Payload type
  "chatbot": {
    "answers": [                                      // An Array of objects that contains all answers to the question. Most of the time, this Array will have length of 1. For optins questions, the Array will have length of the number of propositions.
      {
        "value": "An answer",                         // String | Boolean | Number
        "label":null,                                // If the question is an optin, the label will have a value, null otherwise
        "id": "00000000-0000-0000-0000-000000000000"  // Only exists if the question is an optin, this is the optin ID
      }
    ],
    "question": {                                     // An object with all questions details
      "id": "xxxxxxxx",                               // Question ID
      "value": "Now your email please",               // The question value
      "attribute": "email",                           // The attribute
      "type": "text"                                  // The question type
    }
  }
}
J

Now, here's the payload sent in response to an optin type question:

{
  "createdAt": "0000-00-00T00:00:00.000Z",            // Payload creation date
  "workspaceId": "xxxxxxxx",                          // Fastory's workspaceID
  "visitorId": "xxxxxxxx",                            // Fastory's VisitorID
  "story": {
    "id": "xxxxxxxx",                                 // Story's ID
    "name": "My story's name",                        // Story's name
  },
  "type": "chatbot",                                  // Payload type
  "chatbot": {
    "question": {
      "id": "xxxxxxxx",                               // Question ID
      "value": "Please answer to these optins",       // The question asked
      "type": "optins"                                // Question Type (here optins)
    },
    "answers": [
      {
        "id": "00000000-0000-000x-x000-xx000x0x00xx", // Optin's ID
        "label": "Optin 1",                                                       // Optin's name
        "value":true                                                               // Here the visitor checked this answer
      },
      {
        "id": "00000000-0000-000x-x000-xx000x0x0xxx",
        "label": "Optin 2",
        "value":true
      },
      {
        "id": "00000000-0000-000x-x000-xx000x0xxxxx",
        "label": "Optin 3",
        "value":false
      }
    ]
  }
}

All of a user's information is sent at once. This can result in a rather large payload:

{visitorId: 'xxxxxxxx',
    workspaceId: 'xxxxxxxx',
    story: { id: 'xxxxxxxx', name: '11Mai2021', customId: null },
    optins: {
      optin_allo: {
        id: '00000000-0000-000x-x000-xx000x0xxxxx',
        label: 'allo',
        value: true,
      },
      optin_first_name: {
        id: '00000000-0000-000x-x000-xx000x0xxxxx',
        label: 'first_name',
        value: false,
      },
      optin_Chocolat: {
        id: '00000000-0000-000x-x000-xx000x0xxxxx',
        label: 'Chocolat',
        value: true,
      },
      optin_city: {
        id: '00000000-0000-000x-x000-xx000x0xxxxx',
        label: 'city',
        value: true,
      },
    },

    chatbot: {
      message_dTyNCd8M: {
        answers: { answer_0: { value: 'xxxxxxxx.com', label: null } },
        question: {
          id: 'xxxxxxxx',
          value: 'Quel est votre email?',
          attribute: 'email',
          type: 'text',
        },
      },
      message_RBhT5iev: {
        answers: { answer_0: { value: 'Excellent', label: null } },
        question: {
          id: 'xxxxxxxx',
          value: 'Bonjour ! Comment allez-vous ?',
          attribute: 'single_choice',
          type: 'text',
        },
      },
      message_smZ2Fmej: {
        question: { id: 'xxxxxxxx', value: 'Optin First Name', type: 'optins' },
        answers: {
          answer_0: {
            id: '00000000-0000-000x-x000-xx000x0xxxxx',
            label: 'allo',
            value: true,
          },
          answer_1: {
            id: '00000000-0000-000x-x000-xx000x0xxxxx',
            label: 'first_name',
            value: false,
          },
          answer_2: {
            id: '00000000-0000-000x-x000-xx000x0xxxxx',
            label: 'Chocolat',
            value: true,
          },
        },
      },
      message_67eSfsv5: {
        question: { id: 'xxxxxxxx', value: 'City optin', type: 'optins' },
        answers: {
          answer_0: {
            id: '00000000-0000-000x-x000-xx000x0xxxxx',
            label: 'city',
            value: true,
          },
        },
      },
    },
  "visitor": {
    "email": "john@doe.com",
    "date_of_birth": "2021-04-30",
    "single_choice": "Excellent",
    "phone": "+33600000000",
    "first_name": "John",
    "last_name": "Doe",
    "media": "https://static.fastory.io/uploads/account_id/some_upload_id.jpeg",
    "multiple_choice": "3, 2",
    "terms_conditions": "Accept",
    "rating": "2"
  },
  "games": [
    {
      "type": "memoryGame",
      "score": 5,
      "timeLeft": 39.70499999999995,
      "createdAt": "2021-04-19T13:31:06.418Z",
      "brick": {
        "id": "xxxxxxxx",
        "label": "Memory"
      }
    },
    {
      "type": "swipeQuizGame",
      "score": 70,
      "createdAt": "2021-04-19T13:30:34.885Z",
      "brick": {
        "id": "xxxxxxxx",
        "label": "Swipe Quiz"
      }
    },
    {
      "type": "runnerGame",
      "score": 55,
      "timeLeft": 2992.486000000009,
      "createdAt": "2021-04-19T13:29:26.621Z",
      "brick": {
        "id": "xxxxxxxx",
        "label": "Runner"
      }
    },
    {
      "type": "basketGame",
      "score": 5,
      "timeLeft": 2.7430000000002246,
      "createdAt": "2021-04-19T13:29:06.330Z",
      "brick": {
        "id": "xxxxxxxx",
        "label": "Basket1"
      }
    },
    {
      "type": "footballGame",
      "score": 4,
      "timeLeft": -2.0083333333331868,
      "createdAt": "2021-04-19T13:30:08.915Z",
      "brick": {
        "id": "xxxxxxxx",
        "label": "Football"
      }
    }
  ],
  "contests": [
    {
      "type": "sweepstake",
      "id": "00000000-0000-000x-x000-xx000x0xxxxx",
      "label": "Tirage au sort",
      "status": "live",
      "result": "OPERATOR_PARTICIPATION_ALREADY_SIGNED_UP",
      "reward":null
    },
    {
      "type": "instant_win",
      "id": "00000000-0000-000x-x000-xx000x0xxxxx",
      "label": "Instant Win",
      "status": "ready",
      "result": "already lose",
      "reward":null
    }
  ]
}

Given the length of this payload, here's a typing table that might help you better understand its structure:

OptinsObject {
	[key: string]: {
	  "id"?: ID;
	  "label": string;
	  "value": string | boolean;
	}
}

AnswerObject {
	[key: string]: {
	  "value": string;
	  "label":null | string;
	  "id"?: UUID;
	}
}

QuestionObject
{
  "id": ID;
  "value": string;
  "attribute": string;
  "type": string;
}

ChatbotObject {
	[key: string]: {
	  "answers": AnswerObject;
	  "question": QuestionObject;
	}
}

BrickObject
{
  "id": ID;
  "label": string;
}

GamesObject
{
  "type": string;
  "score": number;
  "timeLeft": float;
  "createdAt": Date;
  "brick": BrickObject;
}

RewardObject
{
  "id": UUID;
  "label": string;
}

ContestsObject
{
  "type": string;
  "id": UUID;
  "label": string;
  "status": "live" | "closed";
  "result": string;
  "reward": RewardObject
}

StoryObject
{
  "id": string;
  "name": string;
}

{
  "visitorId": string;
  "workspaceId": string;
  "story": StoryObject;
  "optins": OptinsObject; // Warning: Optins which was an array of optins is now an object containing all the optins (since June 15, 2022)
  "chatbot": ChatbotObject; // Warning: Chatbot which was an array of optins is now an object containing all the chatbot (since June 15, 2022)
  "visitor": {
    [attributeName: string]: string;
  },
  "games": Array<GamesObject>;
  "contests": Array<ContestObject>;
}
Did this answer your question?
😞
😐
🤩