Account

This is the self-service surface for the calling user themselves — their own profile, sessions, linked social identities, and audit log. Endpoints here always act on the bearer-token holder; there is no :userId path parameter because that would let one user act on another.

For admin-side operations on other users see Users (workspace members) and End users (signed-in users of your OIDC clients).

All endpoints require a bearer access token for the user — see Authentication.

Endpoints

Method Path Purpose
GET /v1/account Profile + workspace memberships
PATCH /v1/account Update name / locale
POST /v1/account/email-change Request an email change
POST /v1/account/password-change Change password
GET /v1/account/sessions List active sessions
POST /v1/account/sessions/:id/revoke Revoke one session
POST /v1/account/sessions/revoke-all Revoke every session except the current one
GET /v1/account/linked-accounts List linked social identities
DELETE /v1/account/linked-accounts/:provider Unlink a provider
GET /v1/account/audit Read the calling user's audit feed

Get profile

GET /v1/account

Returns the user with their workspace memberships and a few derived flags.

{
  "data": {
    "id": "usr_01KPG…",
    "email": "adi@forjio.com",
    "name": "Adhya Pranata Sakti",
    "emailVerified": true,
    "locale": "en-ID",
    "hasPassword": true,
    "mfaEnabled": true,
    "createdAt": "2025-12-04T10:42:00.000Z",
    "lastLoginAt": "2026-05-12T22:01:11.000Z",
    "memberships": [
      {
        "role": "owner",
        "account": {
          "id": "acc_01KPG…",
          "name": "Forjio Headquarters",
          "slug": "forjio-headquarters"
        },
        "joinedAt": "2025-12-04T10:42:00.000Z"
      }
    ]
  }
}

hasPassword: false means the user signs in via social only and there is no password set yet — the change-password endpoint requires currentPassword to be omitted in that case.

Update profile

PATCH /v1/account

Request body

Field Type Description
name string | null Display name. null clears.
locale string (≤10) BCP 47 locale tag. Used to localise emails.

Email and password are managed via the dedicated endpoints below.

Request an email change

POST /v1/account/email-change

Initiates a two-step email change. Huudis sends a verification link to the new address; clicking it actually swaps the email.

Request body

Field Type Required Description
email string yes The new email. Lowercased before insert.
password string yes (if user has one) Re-auth confirmation. Required unless the user has no password (social-only).

Errors include EMAIL_TAKEN (the new email is already a Huudis user), INVALID_CREDENTIALS (wrong password), and SAME_EMAIL.

200 OK returns { pending: true, newEmail }. The old email remains active until the new one is verified.

Change password

POST /v1/account/password-change

Request body

Field Type Required Description
currentPassword string conditional Required if the user already has a password. Omit if hasPassword: false.
newPassword string (≥10) yes Subject to Huudis's strength rules — rejected if WEAK_PASSWORD.

On success, every other session is revoked (the current session is kept so the user isn't kicked out of the tab they just changed their password from). The user gets a "password changed" confirmation email.

List sessions

GET /v1/account/sessions

Returns the calling user's live sessions across every browser, mobile app, and CLI. The current session is flagged current: true.

{
  "data": [
    {
      "id": "sess_01KPG…",
      "userAgent": "Mozilla/5.0 (X11; Linux x86_64) …",
      "ip": "203.0.113.5",
      "createdAt": "2026-05-12T22:01:00.000Z",
      "lastUsedAt": "2026-05-12T23:59:00.000Z",
      "expiresAt": "2026-05-26T22:01:00.000Z",
      "current": true
    }
  ]
}

Revoke a single session

POST /v1/account/sessions/:id/revoke

Kills one session immediately. 400 CANNOT_REVOKE_CURRENT if you targeted your own — use /v1/auth/logout instead for that.

Revoke every other session

POST /v1/account/sessions/revoke-all

Revokes every session except the current one. 200 OK returns { revokedCount: 4 }. Useful as a "lost my phone" panic button.

Linked accounts

GET /v1/account/linked-accounts

Returns the user's social-identity links plus whether they have a password.

{
  "data": {
    "hasPassword": true,
    "providers": [
      { "id": "soc_01KPG…", "provider": "google", "email": "adi@gmail.com", "linkedAt": "2026-04-01T00:00:00.000Z" }
    ]
  }
}
DELETE /v1/account/linked-accounts/:provider

Path: :provider is google or apple.

Removes the social-identity link. Sign-in via that provider stops working.

Errors

Status error.code When
400 LAST_SIGNIN_METHOD Removing this link would leave the user with no way to sign in (no password and no other linked provider). Set a password first.
404 LINK_NOT_FOUND No link for that provider.

A confirmation email goes to the user's primary email after a successful unlink.

Audit log

GET /v1/account/audit?limit=50&cursor=…&event=…&outcome=success&since=…&until=…

Returns the calling user's own audit-log entries. Cursor-paginated — pass nextCursor from the previous response. Filters:

Param Description
event Exact event type (e.g. auth.login, account.profile_updated).
outcome success | failure | denied.
since / until ISO 8601 datetime bounds.

Response

{
  "data": {
    "entries": [
      {
        "id": "alog_01KPG…",
        "timestamp": "2026-05-12T22:01:11.000Z",
        "event": "auth.login",
        "outcome": "success",
        "ip": "203.0.113.5",
        "userAgent": "Mozilla/5.0 …",
        "metadata": { "method": "password" }
      }
    ],
    "nextCursor": "alog_01KPF…"
  }
}

Workspace admins get a broader, workspace-scoped audit feed at a separate endpoint (planned). The current /v1/account/audit is strictly the calling user's own entries.

The profile object

Field Type Description
id string (usr_…) The calling user's ID.
email string Primary email.
name string | null Display name.
emailVerified boolean
locale string BCP 47 tag.
hasPassword boolean false for social-only accounts.
mfaEnabled boolean Any verified MFA device.
createdAt ISO 8601
lastLoginAt ISO 8601 | null Last successful sign-in.
memberships array Workspace memberships with role + account.

Self-service vs. admin paths

Self-service (/v1/account/*) Admin (/v1/iam/* or /v1/ops/*)
Required token The user's own bearer A workspace admin's bearer
Acts on The token holder A different user, by :id
Email change Two-step (link to new address) Direct — admins can force-verify
Password change Requires current password Admin reset (sends new temp)
Session revoke Own sessions only n/a — admins can revoke OIDC refresh tokens, not Huudis sessions, for end users

Don't try to admin yourself with the admin paths — the role guards prevent owner self-demote and member self-remove. Use the self-service paths.

Events

Event type Fires on
huudis.session.created.v1 A new session (web or OIDC) is issued.
huudis.session.revoked.v1 A session (or refresh token) is revoked.

Profile changes, password changes, and email changes don't emit webhook events — they're audit-log-only.

Next

  • Users — admin-side workspace member management.
  • MFA — enrol and manage second factors.
  • Workspaces — switching the active workspace.