Go SDK · Minter
Use the minter when your application's frontend wants to display events directly — see Embed Tokens for the full model. Construct once at boot, mint per request:
m := es.NewMinter()
The returned token from MintToken is a JWT. Hand it to your frontend over your normal page-render path. Never expose your project API key to the browser.
Single-tenant
Use when your project has one customer per project, or when you're embedding the UI in your own internal tooling. The token has no tenant_id claim, so the embed shows every event in the project.
func tokenHandler(m *minter.Client) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if _, err := authenticate(r); err != nil {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
token, err := m.MintToken(r.Context(), minter.TokenOptions{
// No TenantID — the project has one customer (or is
// internal), so the token doesn't need to filter by tenant.
ExpiresIn: time.Hour,
AllowedColumns: []string{"occurred_at", "action", "actor", "target"},
})
if err != nil {
http.Error(w, "mint failed", http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(map[string]string{"token": token})
}
}
Multi-tenant
Use when your project partitions events by tenant_id and each customer should only see their own. Mint a token whose TenantID matches the signed-in user's tenant — the server enforces the scope on every read.
func tokenHandler(m *minter.Client) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
user, err := authenticate(r)
if err != nil {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
token, err := m.MintToken(r.Context(), minter.TokenOptions{
// TenantID set to the signed-in user's tenant — reads
// filtered server-side. For an admin view (your internal
// support tool that should see every tenant), use the
// same minter but omit TenantID.
TenantID: user.TenantID,
ExpiresIn: time.Hour,
AllowedColumns: []string{"occurred_at", "action", "actor", "target"},
AllowedActions: []string{"user.*", "billing.invoice.created"},
})
if err != nil {
http.Error(w, "mint failed", http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(map[string]string{"token": token})
}
}
Multi-tenant products often expose two endpoints:
| Endpoint | Token | Used for |
|---|---|---|
/api/embed-token/customer |
TenantID = signed-in user's tenant |
the end-customer's view of their own audit trail |
/api/embed-token/admin |
TenantID omitted |
internal admin / support tool, shows every tenant |
Same minter, different scoping rules.
TokenOptions
| Field | Type | Notes |
|---|---|---|
TenantID |
string |
Optional. Scopes reads to events with matching tenant_id. Trimmed; ≤ 256 chars. Omit for unscoped (single-tenant or admin view). |
ExpiresIn |
time.Duration |
Server clamps to [60s, 24h]. Zero uses server default (1h). |
AllowedColumns |
[]string |
Optional whitelist of Event JSON field names (snake_case). Empty slice rejected. |
AllowedActions |
[]string |
Exact match or suffix wildcard (user.*). Empty slice rejected. |