End users

An end user is a person who signs into your product through Huudis — not a member of your Huudis workspace. They appear in your Huudis admin surface because their OIDC client belongs to your workspace. The Go SDK exposes the ops namespace as client.EndUsers. For the data model see Concepts → End user; for the HTTP surface see API → End users.

Workspace members (people who can administer Huudis itself) are a different surface — see Users.

Field on the Client

client.EndUsers — type *huudis.EndUsersResource. Every method requires the caller to hold the huudis:ops:end-users:* scope — typically granted via the canned OpsSupport policy attached to your support team's group.

Methods

List

Signature. func (r *EndUsersResource) List(ctx context.Context, p ListEndUsersParams, auth RequestAuth) (*ListPage[EndUser], error)

Cursor-paginated. Search matches email or name (case-insensitive prefix). Default page size 25, max 100.

page, err := client.EndUsers.List(ctx, huudis.ListEndUsersParams{
    Search: "alice",
    Limit:  50,
}, huudis.RequestAuth{})
if err != nil {
    return err
}
for _, u := range page.Items {
    state := "active"
    if u.Disabled {
        state = "disabled"
    }
    last := u.LastLoginAt
    if last == "" {
        last = "never"
    }
    fmt.Println(u.Email, state, last)
}

Get

Signature. func (r *EndUsersResource) Get(ctx context.Context, id string, auth RequestAuth) (*EndUser, error)

Fetches one end user by ID.

u, err := client.EndUsers.Get(ctx, "eu_01HX...", huudis.RequestAuth{})

Revoke

Signature. func (r *EndUsersResource) Revoke(ctx context.Context, id string, auth RequestAuth) error

Revokes every active session for this end user. Refresh tokens are killed; existing access tokens are blocklisted (propagated via JWKS rotation within one minute). The user's record stays; their next attempt to use the product forces a re-login.

if err := client.EndUsers.Revoke(ctx, "eu_01HX...", huudis.RequestAuth{}); err != nil {
    return err
}

Use after credential compromise or password reset.

SendPasswordReset

Signature. func (r *EndUsersResource) SendPasswordReset(ctx context.Context, id string, auth RequestAuth) error

Emails the user a tokenised reset link valid for one hour. Idempotent — repeated calls within the TTL re-use the same token.

_ = client.EndUsers.SendPasswordReset(ctx, "eu_01HX...", huudis.RequestAuth{})

VerifyEmail

Signature. func (r *EndUsersResource) VerifyEmail(ctx context.Context, id string, auth RequestAuth) error

Force-marks the user's email as verified, bypassing the confirm-by-email round-trip. Reserved for support flows where the user has proven ownership out-of-band.

_ = client.EndUsers.VerifyEmail(ctx, "eu_01HX...", huudis.RequestAuth{})

Impersonate

Signature. func (r *EndUsersResource) Impersonate(ctx context.Context, id string, in *ImpersonateInput, auth RequestAuth) (*ImpersonationSession, error)

Mints an access token that authenticates as the end user — used for "log in as this customer" support flows. The returned token's claims include act (the actor who impersonated) and actReason; every action taken under it is audit-logged with both identities.

session, err := client.EndUsers.Impersonate(ctx, "eu_01HX...", &huudis.ImpersonateInput{
    Reason: "ticket #1234 — user reports broken billing page",
}, huudis.RequestAuth{})
if err != nil {
    return err
}
log.Println(session.SessionID, session.ExpiresAt)
// Use session.AccessToken to call your product as the user.

Impersonation is logged. Every impersonation session emits huudis.ops.impersonation_started.v1 and huudis.ops.impersonation_ended.v1 audit events with the actor's identity, the target, the reason, and a session ID linking the two. Don't hide the reason field — it shows up on the customer-facing "an admin viewed your account" surface.

StopImpersonation

Signature. func (r *EndUsersResource) StopImpersonation(ctx context.Context, auth RequestAuth) error

Terminates the impersonation session on the calling actor's identity. Idempotent; safe to call when no session is active.

_ = client.EndUsers.StopImpersonation(ctx, huudis.RequestAuth{})

Disable / Enable

func (r *EndUsersResource) Disable(ctx context.Context, id string, in *DisableEndUserInput, auth RequestAuth) error
func (r *EndUsersResource) Enable(ctx context.Context, id string, auth RequestAuth) error

Disable sets disabled=true, blocks future sign-ins, revokes active sessions. Pair with a reason for the audit log. Enable reverses it (doesn't restore sessions; the user signs in fresh).

_ = client.EndUsers.Disable(ctx, "eu_01HX...", &huudis.DisableEndUserInput{
    Reason: "fraud_review",
}, huudis.RequestAuth{})

// Later:
_ = client.EndUsers.Enable(ctx, "eu_01HX...", huudis.RequestAuth{})

Types

type EndUser struct {
    ID            string `json:"id"`            // "eu_..."
    AccountID     string `json:"accountId"`
    Email         string `json:"email"`
    Name          string `json:"name,omitempty"`
    EmailVerified bool   `json:"emailVerified"`
    Disabled      bool   `json:"disabled"`
    CreatedAt     string `json:"createdAt"`     // ISO-8601
    LastLoginAt   string `json:"lastLoginAt,omitempty"`
}

type ImpersonationSession struct {
    SessionID   string `json:"sessionId"`
    AccessToken string `json:"accessToken"`
    ExpiresAt   string `json:"expiresAt"`
}

See API → End users for field-level rules.

Common patterns

func findEndUser(ctx context.Context, c *huudis.Client, query string) ([]huudis.EndUser, error) {
    page, err := c.EndUsers.List(ctx, huudis.ListEndUsersParams{
        Search: query, Limit: 10,
    }, huudis.RequestAuth{})
    if err != nil {
        return nil, err
    }
    return page.Items, nil
}

Scoped impersonation

Wrap in defer so an early return doesn't leave the session open:

func actAs(ctx context.Context, c *huudis.Client, userID, reason string, fn func(token string) error) error {
    session, err := c.EndUsers.Impersonate(ctx, userID, &huudis.ImpersonateInput{Reason: reason}, huudis.RequestAuth{})
    if err != nil {
        return err
    }
    defer func() { _ = c.EndUsers.StopImpersonation(context.Background(), huudis.RequestAuth{}) }()
    return fn(session.AccessToken)
}

Walk every end user

func allEndUsers(ctx context.Context, c *huudis.Client) ([]huudis.EndUser, error) {
    var out []huudis.EndUser
    var cursor string
    for {
        page, err := c.EndUsers.List(ctx, huudis.ListEndUsersParams{Limit: 100, Cursor: cursor}, huudis.RequestAuth{})
        if err != nil {
            return out, err
        }
        out = append(out, page.Items...)
        if page.NextCursor == "" {
            return out, nil
        }
        cursor = page.NextCursor
    }
}

Errors

Code Status Cause
not_found 404 End-user ID doesn't exist or lives in another workspace.
state_conflict 409 Disabling an already-disabled user; impersonating while another impersonation is active.
forbidden 403 Caller lacks huudis:ops:end-users:* scope.
rate_limited 429 SendPasswordReset capped at 5/hour per user.

Next