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.