End users

An end user is someone who signed into one of your OIDC clients — the people using your product, not the people administrating your Huudis workspace. They have a usr_… ID just like workspace members, but they don't have a Huudis dashboard login; they only ever see Huudis through your app's sign-in screen.

This page covers the admin API for the end-user directory: listing, viewing details, revoking, sending password resets, force-verifying emails, and impersonating for support.

The list is workspace-scoped: an end user is "in" your workspace if and only if they hold a live OIDC consent for at least one of your workspace's OIDC clients.

All endpoints require a bearer admin JWT — see Authentication.

Endpoints

Method Path Purpose
GET /v1/ops/end-users List end users of the active workspace
GET /v1/ops/end-users/:id Detail view
POST /v1/ops/end-users/:id/revoke Revoke consents + refresh tokens (kicks them out of your app)
POST /v1/ops/end-users/:id/send-password-reset Trigger a Huudis-hosted reset email
POST /v1/ops/end-users/:id/verify-email Force-verify the user's email (skip the link click)
POST /v1/ops/end-users/:id/impersonate Start a short-lived session as the user (support tool)
POST /v1/ops/end-users/stop-impersonation Exit back to the operator's own session
POST /v1/ops/end-users/:id/disable Forjio-internal only. Globally disable a Huudis user.
POST /v1/ops/end-users/:id/enable Forjio-internal only. Re-enable.

List end users

GET /v1/ops/end-users

Returns users who hold at least one live consent for an OIDC client owned by the active workspace. Sorted by firstSignInAt descending.

Response

{
  "data": [
    {
      "id": "usr_01KPG…",
      "email": "ibu.rina@cafe-sumur.id",
      "name": "Ibu Rina",
      "emailVerified": true,
      "disabled": false,
      "createdAt": "2026-03-04T11:00:00.000Z",
      "lastLoginAt": "2026-05-12T17:55:00.000Z",
      "firstSignInAt": "2026-03-04T11:02:00.000Z",
      "clientCount": 2
    }
  ]
}

clientCount is the number of your workspace's OIDC clients this user has consented to — useful for spotting "power users" of your suite.

Retrieve an end user

GET /v1/ops/end-users/:id

Returns the user's profile plus the per-client consent list scoped to your workspace. Other workspaces' consents are not included — Huudis never leaks them across customers.

Response

{
  "data": {
    "id": "usr_01KPG…",
    "email": "ibu.rina@cafe-sumur.id",
    "name": "Ibu Rina",
    "emailVerified": true,
    "disabled": false,
    "disabledAt": null,
    "mfaEnabled": false,
    "createdAt": "2026-03-04T11:00:00.000Z",
    "lastLoginAt": "2026-05-12T17:55:00.000Z",
    "consents": [
      {
        "id": "ocs_01KPG…",
        "clientId": "oc_meja_studio",
        "clientName": "MejaStudio",
        "scopes": ["openid", "profile", "email"],
        "consentedAt": "2026-03-04T11:02:00.000Z",
        "revokedAt": null
      }
    ]
  }
}

Errors

Status error.code When
404 NOT_FOUND No user with that ID, or the user has no relationship with your workspace. Huudis returns 404 rather than 403 to avoid leaking the user's existence.

Revoke an end user

POST /v1/ops/end-users/:id/revoke

Cuts the user off from your workspace's clients. Internally:

  • All non-revoked consents for clients owned by the active workspace are stamped with revokedAt = now.
  • All non-revoked refresh tokens issued by those clients are revoked, so the user can't silently mint new access tokens.

Existing access tokens stay valid until they naturally expire (typically ≤1 hour). The user can still sign in again later if they want to re-grant consent — revoke is reversible from the user's side.

204 No Content.

Send a password reset

POST /v1/ops/end-users/:id/send-password-reset

Triggers the standard Huudis password-reset flow on the user's behalf. Huudis emails a reset link; the user clicks it and lands on the Huudis-hosted password-reset page. Useful for support cases where the user can't find the reset button themselves.

You only see { "sent": true } — the token is never returned to you, even as an admin, to prevent unauthorized password resets.

Force-verify the user's email

POST /v1/ops/end-users/:id/verify-email

Sets emailVerified: true without requiring the user to click the verification link. Use sparingly — this skips the normal proof-of-control check. Audit-logged as ops.end_user.email_force_verified.

200 OK{ "emailVerified": true }.

Impersonate a user

POST /v1/ops/end-users/:id/impersonate

Starts a short-lived session as the target user, useful for reproducing support issues. The new session is tagged with impersonatedById = <your-userId> so the user's audit log shows the operator clearly.

Request body

Field Type Description
durationSeconds integer (60–3600) How long the session lasts. Default 1800 (30 minutes).

Response

{
  "data": {
    "impersonating": {
      "userId": "usr_01KPG…",
      "email": "ibu.rina@cafe-sumur.id"
    },
    "expiresAt": "2026-05-13T00:25:00.000Z"
  }
}

A Set-Cookie: huudis_session=… header replaces the calling browser's session with the impersonated one. Subsequent requests from that browser act as the target user.

Errors

Status error.code When
400 NESTED_IMPERSONATION The calling session is already an impersonation. Stop first, then start fresh.
400 USER_DISABLED Target user is globally disabled.
404 NOT_FOUND User isn't an end user of your workspace.

Impersonation is loud in the audit log. Both the operator's audit feed and the target user's audit feed get a ops.end_user.impersonation_started entry with the operator's identity. The target also sees a "Someone signed in to your account" email if their settings have email notifications enabled.

Stop impersonation

POST /v1/ops/end-users/stop-impersonation

Revokes the impersonation session. The operator must sign in again normally to get back into their admin session — stopping doesn't restore the previous cookie.

204 No Content. 400 NOT_IMPERSONATING if the calling session isn't an impersonation.

Disable / enable (Forjio-internal)

POST /v1/ops/end-users/:id/disable
POST /v1/ops/end-users/:id/enable

Globally disables (or re-enables) a Huudis user across every workspace. Reserved for Forjio operations — callers must be in the special huudis Forjio-internal workspace and have role owner or admin. Returns 403 FORBIDDEN for everyone else.

Disabling cascades:

  • The user's disabled flag flips to true.
  • All live sessions are revoked immediately.
  • All refresh tokens are revoked — no Forjio product can mint a new access token for them.

The user's data is preserved — this is a soft suspend, not a delete.

The end-user object

Field Type Description
id string (usr_…) Same Huudis user ID format as workspace members.
email string Sign-in email.
name string | null Display name.
emailVerified boolean Whether the email has been confirmed (via link or admin force).
disabled boolean Globally disabled flag.
disabledAt ISO 8601 | null When the user was disabled.
mfaEnabled boolean Whether the user has at least one verified MFA device.
createdAt ISO 8601 User row creation.
lastLoginAt ISO 8601 | null Most recent successful sign-in across any client.
firstSignInAt ISO 8601 First sign-in to your workspace's clients (list only).
clientCount integer Number of your workspace's clients the user has consented to (list only).
consents array Per-client consent records, scoped to your workspace (detail only).

Cross-workspace isolation

The end-user surface is strictly workspace-scoped. If a user signs into MejaStudio (your workspace) and also into Plugipay (Forjio's workspace), each workspace sees the user as an end user only through their own clients. The Huudis usr_… is shared, but:

  • MejaStudio admins cannot list Plugipay end users.
  • Revoke at MejaStudio only revokes MejaStudio consents; the Plugipay session keeps working.
  • The user's globally disabled flag is the only thing that affects every workspace at once — and only Forjio-internal operators can flip it.

End users vs. workspace members

Property Workspace member End user
Can log into the Huudis dashboard yes no
Listed in /v1/iam/users yes no
Listed in /v1/ops/end-users only if they also consented to a workspace client yes
Can hold IAM policies yes no
Has a role (owner/admin/member) yes no (just consents)

The same usr_… can be both, depending on which clients they've consented to.

Events

Event type Fires on
huudis.user.created.v1 A new end user is provisioned (first sign-up via your client).
huudis.user.disabled.v1 An end user is globally disabled.
huudis.user.enabled.v1 A previously disabled user is re-enabled.
huudis.oidc.consent_granted.v1 An end user consents to one of your clients for the first time.

End-user revoke and impersonation do not emit webhook events — they're audit-log only.

Next

  • Consents — the user-side view of OIDC consents.
  • OIDC clients — the clients end users consent to.
  • Users — workspace members (the other kind of user).