Support APIQuick starts by stack

Quick start: Go (`net/http`)

Go ≥ 1.21, standard library only. About 15 minutes to a verified chat message.

go get github.com/gauge-chat/pulse/packages/sdk-go
export GAUGE_APP_ID=<app id> GAUGE_IDENTITY_SECRET=<identity secret> GAUGE_WEBHOOK_SECRET=<whsec_…>

1 and 3. Two handlers

userFrom(r) is your auth: the signed-in user from your session middleware, or nil.

package main

import (
	"encoding/json"
	"log"
	"net/http"
	"os"
	"sync"

	gaugesupport "github.com/gauge-chat/pulse/packages/sdk-go"
)

func gaugeToken(w http.ResponseWriter, r *http.Request) {
	user := userFrom(r)
	if user == nil {
		http.Error(w, "sign in first", http.StatusUnauthorized)
		return
	}
	token, err := gaugesupport.MintToken(os.Getenv("GAUGE_IDENTITY_SECRET"), gaugesupport.MintInput{
		AppID:       os.Getenv("GAUGE_APP_ID"),
		UserID:      user.ID,
		Integration: "customer",
		Email:       user.Email,
	})
	if err != nil {
		http.Error(w, "could not mint", http.StatusInternalServerError)
		return
	}
	w.Header().Set("Cache-Control", "no-store")
	w.Header().Set("Content-Type", "application/json")
	_ = json.NewEncoder(w).Encode(map[string]string{"token": token})
}

var handled sync.Map // use your database in production: ids repeat on retries and resends

// gaugeWebhook takes options only so tests can fix the clock; production passes none.
func gaugeWebhook(opts ...gaugesupport.WebhookOption) http.HandlerFunc {
	secrets := []string{os.Getenv("GAUGE_WEBHOOK_SECRET")}
	return func(w http.ResponseWriter, r *http.Request) {
		v, err := gaugesupport.VerifyWebhookRequest(r, secrets, opts...)
		if err != nil || !v.Valid {
			http.Error(w, v.Reason, http.StatusBadRequest)
			return
		}
		if v.Event != nil {
			if _, seen := handled.LoadOrStore(v.Event.ID, true); !seen {
				log.Printf("gauge %s %s", v.Event.Event, v.Event.ID)
			}
		}
		w.WriteHeader(http.StatusOK)
	}
}
func main() {
	http.HandleFunc("GET /gauge-token", gaugeToken)
	http.HandleFunc("POST /gauge/webhooks", gaugeWebhook())
	log.Fatal(http.ListenAndServe(":8080", nil))
}

("GET /path" patterns need Go 1.22; on 1.21 use plain paths and check r.Method.)

2. The widget (your page)

<script src="https://widget.gauge.chat/v1/<version>/widget.js"
  integrity="sha384-<integrity>" crossorigin="anonymous" async></script>
<script>
  window.addEventListener('load', function () {
    window.PulseWebchat.init({
      apiUrl: 'https://app.gauge.chat/backend',
      connectionId: '<chat connection id>',
      supportMode: true,
      integration: 'customer',
      getToken: function () { // called on every open, so the token is always fresh
        return fetch('/gauge-token', { credentials: 'include' })
          .then(function (r) { return r.json(); })
          .then(function (j) { return j.token; });
      },
    });
  });
</script>

CSP: script-src https://widget.gauge.chat and connect-src https://app.gauge.chat wss://app.gauge.chat. Signed-out visitors: leave out getToken; they chat anonymously where the Integration allows it. Check my site (Developers → Support) fetches your page and tells you if the snippet, its integrity and the connection id are right.

4. Check it

  1. Sign in to your site as a test user (in the sandbox: Seed test customers, then sign in as test_customer_1), open the widget and send "hello". In the console the conversation shows the user's name and Verified. Not verified? Paste the JSON from /gauge-token into Developers → Support → Token checker: it names the rule that failed.
  2. Developers → Webhooks → your endpoint → Send test event, pick support.chat.message_created: the result says Delivered · 200 and your log shows the event id. Change one byte of the secret in your env and send again: your handler answers 400 bad_signature.

Server was down? Replay failed on the same page resends everything that did not land.

On this page