fastory

Integrations

How to add a webhook to Fastory?

Learn how to enable and configure a webhook on Fastory to automatically send your campaign data (scores, form answers, opt-ins, contest entries) to any URL in real time.

A webhook lets you automatically transfer, in real time, the data collected by your Fastory campaigns to the URL of your choice. This article explains what this feature is for, how to enable it, and how to configure it.

What is it for?

A webhook is a simple, automated way to transfer the data collected in your Fastory campaigns (contests, quizzes, interactive experiences) to a custom URL. It acts as a bridge between Fastory and your external systems: with every participation, the user's answers and data (score, form information, etc.) are automatically sent to the URL you have defined.

In practice, this means you can:

  • Store scores on your own server.
  • Sync user data with your CRM or marketing automation tool.
  • Trigger a custom script (for example, sending an automatic email).
  • Connect Fastory to tools like Zapier to multiply integrations without complex development.

You will typically use a webhook when you need to process the data on your side (storing scores on your server, syncing, user login, etc.) or trigger a script on specific questions.

Enable the Webhook add-on

The webhook is an add-on: it first needs to be enabled on your account.

  1. From your experiences, open the Add-ons page.
  1. Click the search bar and type Webhook.
  1. If you don't have access to this add-on, contact our team via the Contact us button and fill in the activation request form. We will then get back to you (the webhook can be added as an option to your subscription).

Configure the webhook

Once the add-on is enabled, go to your workspace home page, Experience tab:

  1. In the left navigation bar, open the Integrations tab.
  1. Click the Create an integration button (that is, a webhook).
  1. A configuration window appears. Fill in the following fields:
      • Integration title: give it a distinctive title to make managing your webhooks easier.
      • Endpoint (URL): enter the target URL of the integration.
      • Token (optional): enter a token if needed, for authentication or other processing.
  1. Click Test connection to send a test payload to the endpoint and validate the connection.

Example of a test payload:

{
  "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"
    }
  }
}

Choose the target

You can select the target of the transmitted data:

  • bot: receive the answers generated by a bot following a predefined question.
  • story_bot: in addition to the bot's answers, get the URL parameters as well as the game participations of the story following a predefined question.
  • workspace: define a global setting for your workspace. Currently, this only lets you choose after which opt-in the data will be transmitted.

Types of transmitted data (payloads)

There are three types of payloads:

  1. games: gathers all participations in the various games (runner, basketball, football, quiz, etc.).
  1. chatbot: covers all answers to questions asked by the bot, including opt-ins.
  1. contest: includes all contest participations, whether an Instant Win or a prize draw.

Games payload

List of available 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"
    }
  }
}

For Selfie games, the participation and vote-type participations are also transmitted. Please note: when a player votes, id is the ID of the vote and vote is the ID of the participation the user voted for.

Chatbot payload

Here is the payload sent following an answer to a question of type email:

{
  "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
    }
  }
}

And here is the payload sent following an answer to a question of type optin:

{
  "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 in one go. This can result in a fairly 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,
    },
  ],
}

Since this payload is a bit long, here is a typing table to 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>;
}

Best practices

  • Give each integration a distinctive title so you can find and manage it easily.
  • Always use Test connection before validating, to check that your endpoint receives the data correctly.
  • Secure your webhook by providing an authentication token and validating the origin of the requests.
  • Choose the target (bot, story_bot, or workspace) that matches what you actually want to receive.
  • Make sure your endpoint accepts POST requests and can process the JSON format sent by Fastory.

Concrete use cases

If you want to store scores on your server

Set up a webhook, then use the games payload: it contains the game type, the score, and the participation date for every game played in your experience.

If you want to sync your leads with your CRM

Retrieve the visitor's information and the answers to questions (chatbot payload and visitor object) to automatically feed your CRM or marketing automation tool with every participation.

If you want to trigger an automatic email

Use the webhook to trigger a custom script (for example, on the answer to a specific question) that will automatically send an email to your participant.

If you want to connect Fastory to Zapier

Enter the URL provided by Zapier as your webhook's endpoint: you can then connect Fastory to hundreds of third-party tools without complex development.

FAQ

How does a webhook work on Fastory?

A webhook automatically sends the data collected by your campaign (form, score, answers) to the URL of your choice via a POST request.

What is the best tool to test my Fastory webhook?

Zapier is an excellent solution to test your webhook without any development.

Can I send only certain data via the webhook?

Yes. You can select which types of data to transmit (form, game results, answers to questions) by choosing the appropriate target and payload.

How do I secure my webhook?

Add an authentication token in the URL or validate the origin of the requests using the headers sent by Fastory.

Why use a webhook rather than a CSV export?

The webhook works in real time and automatically, whereas the CSV export requires a manual action and is not instantaneous.

Does the webhook work with all Fastory games?

Yes. It can be enabled on all interactive experiences (quiz, Instant Win, casual games, etc.).

Do I need to enable a specific add-on?

Yes. The Webhook add-on must be enabled by the Fastory team upon request.

Did this answer your question?
😞
😐
🤩