The snippet below is exactly what the dashboard generates under Project > Settings > Identity when you choose Go. Copy it from the dashboard to get your real project ID and audience URL pre-filled.
Install
go get github.com/golang-jwt/jwt/v5
Mint a token
// go get github.com/golang-jwt/jwt/v5
import (
"os"
"time"
jwt "github.com/golang-jwt/jwt/v5"
)
type reqioClaims struct {
Email string `json:"email"`
Traits map[string]any `json:"traits"`
jwt.RegisteredClaims
}
now := time.Now()
claims := reqioClaims{
Email: user.Email,
Traits: map[string]any{"plan": user.Plan, "amount": user.PlanAmount, "interval": user.BillingInterval}, // amount in cents; interval "month" | "year"
RegisteredClaims: jwt.RegisteredClaims{
Subject: user.ID,
Audience: jwt.ClaimStrings{"https://reqio.app/p/YOUR_PROJECT_ID"},
IssuedAt: jwt.NewNumericDate(now),
ExpiresAt: jwt.NewNumericDate(now.Add(5 * time.Minute)),
},
}
token, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).
SignedString([]byte(os.Getenv("REQIO_SECRET")))
Replace YOUR_PROJECT_ID with your project ID, or copy the full snippet from the dashboard where the audience URL is pre-filled.
Environment variable
Set REQIO_SECRET to the signing secret from Project > Settings > Identity > Reveal secret. Store it in an environment variable managed by your deployment platform; never include it in responses.
HTTP handler example
package main
import (
"net/http"
"os"
"text/template"
"time"
jwt "github.com/golang-jwt/jwt/v5"
)
type reqioClaims struct {
Email string `json:"email"`
Traits map[string]any `json:"traits"`
jwt.RegisteredClaims
}
var tmpl = template.Must(template.ParseFiles("templates/page.html"))
func pageHandler(w http.ResponseWriter, r *http.Request) {
user := userFromSession(r) // your own session resolution
if user == nil {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
now := time.Now()
claims := reqioClaims{
Email: user.Email,
Traits: map[string]any{"plan": user.Plan, "amount": user.PlanAmountCents, "interval": user.BillingInterval},
RegisteredClaims: jwt.RegisteredClaims{
Subject: user.ID,
Audience: jwt.ClaimStrings{os.Getenv("REQIO_AUDIENCE")},
IssuedAt: jwt.NewNumericDate(now),
ExpiresAt: jwt.NewNumericDate(now.Add(5 * time.Minute)),
},
}
token, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).
SignedString([]byte(os.Getenv("REQIO_SECRET")))
if err != nil {
http.Error(w, "identity error", http.StatusInternalServerError)
return
}
data := map[string]string{
"ProjectID": os.Getenv("REQIO_PROJECT_ID"),
"ReqioToken": token,
}
tmpl.Execute(w, data)
}
<!-- templates/page.html -->
<script
src="https://reqio.app/widget.js"
data-project-id="{{ .ProjectID }}"
data-identity-token="{{ .ReqioToken }}"
async
></script>
Store the audience URL in REQIO_AUDIENCE so you do not hardcode it.
Pass the token to the widget
For server-rendered HTML, pass the token via data-identity-token:
<script
src="https://reqio.app/widget.js"
data-project-id="YOUR_PROJECT_ID"
data-identity-token="{{ .ReqioToken }}"
async
></script>
For SPAs where the user authenticates after the initial page load, call window.Reqio.identify() from JavaScript after login:
window.Reqio.identify(token);
Traits reference
| Field | Type | Description |
|---|---|---|
| plan | string | Plan name on your billing system, e.g. "pro". |
| amount | number | Recurring charge as billed, in cents. For annual plans send the full annual total; Reqio divides by 12 to derive the monthly value. |
| interval | "month" or "year" | Billing cadence for amount. |
Security
Store REQIO_SECRET in an environment variable and never include it in responses. The JWT is short-lived (5 minutes), signed with HS256, and audience-bound to one Reqio project URL, limiting the damage from a leaked token. The signing secret must remain server-side.