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
- 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-tokeninto Developers → Support → Token checker: it names the rule that failed. - 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 400bad_signature.
Server was down? Replay failed on the same page resends everything that did not land.
Quick start: Rails
Rails 7+, Ruby ≥ 3.0. About 15 minutes to a verified chat message.
Signed integration tokens
An integration token tells Gauge who your user is and which Integration of your App they are on (for example customer or salon). Your server mints it; the widget, WebView or portal passes it on.…