Prerequisites

Before setting up Gorgias integration, ensure you have:

Gorgias Account

  • Active Gorgias account with API access
  • Gorgias API credentials (client ID, client secret, refresh token)
  • Admin permissions to install integrations

Vector Database

  • Pinecone account and API key for vector storage
  • Pinecone index created with appropriate dimensions
  • Understanding of vector embeddings concepts

AI Services

  • OpenAI API key for embeddings generation
  • Understanding of similarity search principles
  • Familiarity with RAG (Retrieval Augmented Generation)

Technical Setup

  • Node.js 16+ installed
  • Basic understanding of REST APIs and JavaScript
  • Access to your Gorgias ticket data for testing

Quickstart Guide

This is a quickstart guide to get you up and running with the Gorgias Ticket Embeddings. This guide will walk you through the steps to get your Gorgias ticket data into Pinecone and create embeddings for each ticket.

Authentication

Gorgias Ticket Embeddings

Once you have installed the app we can create embeddings and store your Gorgias ticket data in Pinecone, you can create embeddings for each ticket. Embeddings are vector representations of your ticket data. You can use these embeddings to find similar tickets or to create a recommendation system.

1

Loop Through Tickets

Loop Through Gorgias Ticket Data

2

Create Embeddings

Create embeddings for each ticket and message, and store in Pinecone

3

Store Chunks of Ticket Data

Store the vector representation of the ticket data in Pinecone and associated metadata with the ticket data


// Main function to create tickets feed
const createTicketFeed = async (req, res) => {

    console.log('Creating Ticket Feed');

    var pinecone_index = "";
    var pinecone_api_key = "";
    var pinecone_namespace = "";
    var shop = "";
    var shopify_access_token = "";
    var account = "";

    try {

        var client_id = ""
        var client_secret = ""

        async function refreshToken(account, clientId, clientSecret, refreshToken) {

            const data = new URLSearchParams({
                grant_type: 'refresh_token',
                refresh_token: refreshToken,
            }).toString();

            const config = {
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                    'Authorization': `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString('base64')}`,
                },
            };

            return axios.post(`https://${account}.gorgias.com/oauth/token`, `grant_type=refresh_token&refresh_token=${refreshToken}`, config)
                .then((response) => {
                    console.log('Refresh Token Response:', response.data.access_token);
                    return response.data;
                })
                .catch((error) => {
                    console.error(error);
                });
        };

        const token = await refreshToken(account, client_id, client_secret, gorgiasRefreshToken);

        const limit = 5;
        const maxPage = 1000;

        const requestOptions = {
            method: 'GET',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${token.access_token}`,
            },
        };

        const viewId = 525587

        const response = await fetch(`https://${account}.gorgias.com/api/tickets?limit=${limit}&viewId=${viewId}`, requestOptions);


        // Get Ticket and the next cursor from response from axios call
        const next_j = await response.json();

        var next_token_j = next_j.meta.next_cursor;

        console.log('cursor', next_token_j);

        await getGorgiasTickets(next_token_j, account, limit, maxPage, token.access_token, pinecone_index, pinecone_api_key, pinecone_namespace);


    } catch (error) {

        console.log('Error: ', error);

        return res.status(503).send({
            success: false,
            error: error.stack,
        });


    }
};

let page = 1;

// Get Shopify Products
async function getGorgiasTickets(nextPageToken, account, limit, maxPage, access_token, pinecone_index, pinecone_api_key, pinecone_namespace) {

    console.log('Getting Next Tokens');

    if (page <= maxPage) {

        const gorgiasRequestOptions = {
            method: 'GET',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${access_token}`,
            },
        };

        console.log('access_token: ', access_token);

        const response = await fetch(`https://${account}.gorgias.com/api/tickets?limit=5&cursor=${nextPageToken}`, gorgiasRequestOptions);

        const tickets_data = await response.json();

        const tickets = tickets_data.data;

        var next_token = tickets_data.meta.next_cursor;

        const documents = [];

        for (const item of tickets) {

            try {

                const id = item.id || null;
                const assignee_user = item.assignee_user || null;
                const channel = item.channel || null;
                const closed_datetime = item.closed_datetime || null;
                const created_datetime = item.created_datetime || null;
                const external_id = item.external_id || null;
                const language = item.language || null;
                const last_message_datetime = item.last_message_datetime || null;
                const last_received_message_datetime = item.last_received_message_datetime || null;
                const opened_datetime = item.opened_datetime || null;
                const snooze_datetime = item.snooze_datetime || null;
                const status = item.status || null;
                const update_datetime = item.update_datetime || null;
                const via = item.via || null;
                const uri = item.uri || null;
                const customer_id = item.customer.id || null;
                const email = item.customer.email || null;
                const name = item.customer.name || null;
                const firstname = item.customer.firstname || null;
                const lastname = item.customer.lastname || null;


                const gorgiasMessagesRequestOptions = {
                    method: 'GET',
                    headers: {
                        'Content-Type': 'application/json',
                        'Authorization': `Bearer ${access_token}`,
                    },
                };


                const messages_responses = await fetch(`https://${account}.gorgias.com/api/messages?ticket_id=${id}&limit=10`, gorgiasMessagesRequestOptions);

                const messages_data = await messages_responses.json();

                console.log('messages_data: ', messages_data);

                const messages = messages_data.data;

                if (messages.length > 0) {

                    for (const message of messages) {

                        let body_text = message.body_text || null;
                        const max_length = 1000;
                    
                        // Check if the body text exceeds the maximum length
                        if (body_text && body_text.length > max_length) {
                            // Shorten the text and assign it back to body_text
                            body_text = body_text.substring(0, max_length);
                            // Log the shortened text
                            console.log("Shortened text:", body_text);
                        }

                        const message_id = message.id || null;
                        const channel = message.channel || null;
                        const created_datetime = message.created_datetime || null;
                        const from_agent = message.from_agent || null;
                        const receiver = message.receiver.name || null;
                        const sender = message.sender.name || null;
                        const subject = message.subject || null;
                        const ticket_id = message.ticket_id || null;
                        const via = message.via || null;


                        let metadata = {
                            id,
                            assignee_user,
                            channel,
                            closed_datetime,
                            created_datetime,
                            external_id,
                            language,
                            last_message_datetime,
                            last_received_message_datetime,
                            opened_datetime,
                            snooze_datetime,
                            status,
                            update_datetime,
                            via,
                            uri,
                            email,
                            customer_id,
                            name,
                            firstname,
                            lastname,
                            message_id,
                            body_text,
                            from_agent,
                            receiver,
                            sender,
                            subject,
                            ticket_id
                        };

                        const document = {
                            id: uuid(),
                            id,
                            metadata,
                        };

                        documents.push(document);

                    }
                }

            } catch (error) {

                console.log(`Error processing item: ${JSON.stringify(item)}`);
                console.log(error);
            }

            for (let i = 0; i < documents.length; i += DOCUMENT_UPSERT_BATCH_SIZE) {

                // Split documents into batches
                var batchDocuments = documents.slice(i, i + DOCUMENT_UPSERT_BATCH_SIZE);

                console.log(batchDocuments);

                // Convert batchDocuments to string
                var batchDocumentString = JSON.stringify(batchDocuments)

                // Remove commas from string
                console.log(batchDocumentString);

                // Create Embeddings
                console.log('Looping through documents...');

                const user_id = "domsteil";

                // OpenAI Request Body
                var raw = JSON.stringify({ "input": batchDocumentString, "model": "text-embedding-3-large", "user": user_id });

                // OpenAI Request Options
                var openaiRequestOptions = {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                        'Authorization': ''
                    },
                    body: raw,
                    redirect: 'follow'
                };

                // Make Callout to OpenAI to get Embeddings
                console.log('Creating Embedding...');

                // Make Callout to OpenAI
                let embeddings_response = await fetch("https://api.openai.com/v1/embeddings", openaiRequestOptions)

                // Create Pinecone Request Body
                const vectors_embeddings = await embeddings_response.json();

                console.log(vectors_embeddings);

                // Create Pinecone Request Body
                var vectors_object = { id: uuid(), values: vectors_embeddings.data[0].embedding, metadata: { "text": batchDocumentString, "user": user_id } };

                console.log(vectors_object);

                var raw = JSON.stringify({ "vectors": vectors_object, "namespace": pinecone_namespace });

                var pineconeRequestOptions = {
                    method: "POST",
                    headers: {
                        "Content-Type": "application/json",
                        "Host": pinecone_index,
                        "Content-Length": 60,
                        "Api-Key": pinecone_api_key,
                    },
                    body: raw,
                    redirect: "follow",
                };

                // Make Callout to Pinecone
                // Pinecone Upsert

                console.log('Upserting Pinecone...');

                let pinecone_query_response = await fetch(`/vectors/upsert`, pineconeRequestOptions)
                    .then(response => response.text())
                    .then(json => {
                        console.log(json);
                    })
                    .catch(error => {
                        console.error(error);
                    });

            }
        }

        page += 1;
        console.log(`On page ${page}, processing next page...`);

        return getGorgiasTickets(next_token, account, limit, maxPage, access_token, pinecone_index, pinecone_api_key, pinecone_namespace);

    } else {

        return
    }

};

Conclusion

In this guide, we walked through the steps to get your Gorgias ticket data into Pinecone and create embeddings for each ticket. You can now use these embeddings to find similar tickets or to create a recommendation system. If you have any questions or need help, please feel free to reach out to us at support@stateset.com