API Integration Guide

This guide covers how to integrate our fingerprinting and open-end tracking features, and how to call our API using both JavaScript and React.

JavaScript Implementation

Step 1: Setup Fingerprinting

Integrate our fingerprinting script using one of these methods:

  1. HTML link (preferred):

    <script src="https://roundtable.ai/js/fingerprinting.js"></script>
    
  2. Dynamic JavaScript insertion:

    const script = document.createElement('script');
    script.src = 'https://roundtable.ai/js/fingerprinting.js';
    document.head.appendChild(script);
    

The script adds fingerprint_id to session storage. Retrieve it with:

const fingerprintId = sessionStorage.getItem('fingerprint_id');

Step 2: Setup Open-end Tracking

Use our JavaScript tracker to record changes in open-ended textboxes:

const textbox_id = 'exampleTextarea';
const max_characters_for_history = 25000;

document.addEventListener('DOMContentLoaded', function () {
    let question_history = [];
    let old_text = document.getElementById(textbox_id).value;
    let text_over_length = false;
    let start_time;

    document.getElementById(textbox_id).addEventListener('input', function (e) {
        // Input handling logic here
    });

    document.getElementById(textbox_id).addEventListener('copy', function (e) {
        // Copy handling logic here
    });
});

Step 3: Call the API

Use the collected data to make an API call:

const apiData = {
  participant_id: "participant_123",
  question_histories: {
    Q1: question_history
  },
  questions: {
    Q1: "How do you feel our software has impacted your daily workflow?"
  },
  responses: {
    Q1: document.getElementById(textbox_id).value
  },
  survey_id: "survey_456",
  fingerprint_id: fingerprintId
};

fetch('https://api.roundtable.ai/alias/latest', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'api_key': '<api_key>'
  },
  body: JSON.stringify(apiData)
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));

React Implementation

Step 1: Setup Fingerprinting

Create a custom hook to load the fingerprinting script and retrieve the fingerprint_id:

import { useState, useEffect } from 'react';

const useFingerprint = () => {
    const [fingerprintId, setFingerprintId] = useState(null);

    useEffect(() => {
        const script = document.createElement('script');
        script.src = 'https://roundtable.ai/js/fingerprinting.js';
        script.async = true;
        script.onload = () => {
            setTimeout(() => {
                const id = sessionStorage.getItem('fingerprint_id');
                setFingerprintId(id);
            }, 100);
        };
        document.head.appendChild(script);

        return () => {
            document.head.removeChild(script);
        };
    }, []);

    return fingerprintId;
};

Step 2: Setup Open-end Tracking

Create a custom hook to track changes in open-ended textboxes:

import { useState, useRef, useCallback } from 'react';

const useQuestionHistory = (maxCharactersForHistory = 25000) => {
    const [history, setHistory] = useState([]);
    const startTimeRef = useRef(null);
    const textOverLengthRef = useRef(false);

    const getTimeElapsed = useCallback(() => {
        if (!startTimeRef.current) {
            startTimeRef.current = Date.now();
            return 0;
        }
        return Date.now() - startTimeRef.current;
    }, []);

    const addHistoryEntry = useCallback((newEntry) => {
        if (textOverLengthRef.current) return;

        setHistory(prevHistory => {
            const updatedHistory = [...prevHistory, newEntry];
            const lengthOfHistory = JSON.stringify(updatedHistory).length;

            if (lengthOfHistory > maxCharactersForHistory) {
                textOverLengthRef.current = true;
                return prevHistory;
            }

            return updatedHistory;
        });
    }, [maxCharactersForHistory]);

    const handleInput = useCallback((event) => {
        const timeElapsed = getTimeElapsed();
        addHistoryEntry({
            s: event.target.value,
            t: timeElapsed
        });
    }, [getTimeElapsed, addHistoryEntry]);

    const handleCopy = useCallback((event) => {
        const timeElapsed = getTimeElapsed();
        addHistoryEntry({
            s: event.target.value,
            t: timeElapsed,
            o: 'c',
            ct: window.getSelection().toString()
        });
    }, [getTimeElapsed, addHistoryEntry]);

    return { history, handleInput, handleCopy };
};

Step 3: Create Open-end Component

import React from 'react';
import useQuestionHistory from './useQuestionHistory';

const OpenEndQuestion = ({ id, question }) => {
    const { history, handleInput, handleCopy } = useQuestionHistory();

    return (
        <div>
            <label htmlFor={id}>{question}</label>
            <textarea
                id={id}
                onChange={handleInput}
                onCopy={handleCopy}
            />
        </div>
    );
};

export default OpenEndQuestion;

Step 4: Setup API Call

Create a custom function for making API calls:

const callApi = async (questionHistories, fingerprintId, responses) => {
    if (!fingerprintId || Object.keys(questionHistories).length === 0) {
        throw new Error('Missing required data');
    }

    const apiData = {
        participant_id: "participant_123", // Replace with actual participant ID
        question_histories: questionHistories,
        questions: {
            // Map of question IDs to question text
            // e.g., Q1: "How do you feel our software has impacted your daily workflow?"
        },
        responses: responses,
        survey_id: "survey_456", // Replace with actual survey ID
        fingerprint_id: fingerprintId
    };

    const response = await fetch('https://api.roundtable.ai/alias/latest', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'api_key': '<api_key>' // Replace with your actual API key
        },
        body: JSON.stringify(apiData)
    });

    if (!response.ok) {
        throw new Error('API call failed');
    }

    return await response.json();
};

Step 5: Implement in a Survey Component

import React, { useState } from 'react';
import OpenEndQuestion from './OpenEndQuestion';
import useFingerprint from './useFingerprint';

const Survey = () => {
    const [questionHistories, setQuestionHistories] = useState({});
    const [responses, setResponses] = useState({});
    const [isSubmitting, setIsSubmitting] = useState(false);
    const [error, setError] = useState(null);
    const [isCompleted, setIsCompleted] = useState(false);
    const fingerprintId = useFingerprint();

    const handleQuestionComplete = (id, history, response) => {
        setQuestionHistories(prev => ({ ...prev, [id]: history }));
        setResponses(prev => ({ ...prev, [id]: response }));
    };

    const handleSubmit = async () => {
        setIsSubmitting(true);
        setError(null);

        try {
            await callApi(questionHistories, fingerprintId, responses);
            setIsCompleted(true);
        } catch (err) {
            setError(err.message);
        } finally {
            setIsSubmitting(false);
        }
    };

    const questions = [
        { id: 'Q1', text: "How do you feel our software has impacted your daily workflow?" },
        { id: 'Q2', text: "What features would you like to see in future updates?" }
    ];

    return (
        <div>
            {questions.map(q => (
                <OpenEndQuestion
                    key={q.id}
                    id={q.id}
                    question={q.text}
                    onComplete={(history, response) => handleQuestionComplete(q.id, history, response)}
                />
            ))}
            <button onClick={handleSubmit} disabled={isSubmitting}>
                Submit Survey
            </button>
            {isSubmitting && <p>Submitting responses...</p>}
            {error && <p>Error: {error}</p>}
            {isCompleted && <p>Survey submitted successfully!</p>}
        </div>
    );
};

export default Survey;

This implementation:

  1. Sets up fingerprinting using a custom hook.
  2. Tracks open-end question responses and histories.
  3. Makes an API call when the submit button is clicked.

Remember to replace placeholder values (e.g. API key, participant ID, survey ID) with actual values in your implementation.