---
title: "Current identity"
method: GET
path: "/auth/me"
tags: ["auth"]
---

# Current identity

`GET /auth/me`

## Request

- Base URL: `https://api.instruxi.dev/api/v1/enforcer`
- URL: `https://api.instruxi.dev/api/v1/enforcer/auth/me`
- Auth: one of:
  - API key in header `Authorization`
  - API key in header `X-API-Key`

The authenticated caller's own identity in one call: account (id, tenant, role, wallet address, login/activity timestamps), permissions, verification sessions plus the derived KYC-verified flag, resolved authz groups (id/slug/name), inline terms-acceptance posture, the caller's referral code, and — for a send-enforced tenant with a smart wallet not yet migrated — a `wallet_send_migration_required` flag. Terms and referral data are best-effort: a lookup hiccup on either omits that field rather than failing the whole call.

## Response `200`

OK

- InternalDeliveryHttpHandlerMeResponse
  - `data` InternalDeliveryHttpHandlerMeData
    - `account_id` string
    - `active` boolean
    - `authenticated_at` string: authenticated_at / last_login_at update only on a fresh Login (not on token refresh); last_active_at is the warm-path freshness signal (throttled).
    - `created_at` string
    - `groups` InternalDeliveryHttpHandlerMeGroup[]: Groups are the authz groups the caller belongs to, resolved to {id, slug, name} so clients can gate on slug + display a label without a separate lookup. No omitempty: always an array (NewMeData seeds []), so a groupless account serializes as [] rather than null/absent.
      - `group_type_id` string: GroupTypeID is the group's taxonomy, so a client can gate on "my groups of type X" without a second call. Omitted for an untyped group. A slug alone is already unambiguous WITHIN a tenant (uk_groups_tenant_slug is on (tenant_id, slug), not (tenant_id, group_type_id, slug)), so a consumer that knows its slugs up front does not need this field — it is for the caller that wants to enumerate one taxonomy it does not have a slug list for.
      - `id` string
      - `name` string
      - `slug` string
    - `is_discoverable` boolean
    - `kyc_verified` boolean: No omitempty: a bool false must serialize so clients can distinguish "not KYC-verified" from "field absent / older server".
    - `last_active_at` string
    - `last_login_at` string
    - `passkey_mfa_verified_at` string: PasskeyMfaVerifiedAt is the server-verified passkey MFA completeness stamp (POST /auth/me/passkey/verify). Clients gate onboarding on this, not on Privy's eventually-consistent client SDK arrays. NULL/absent = never server-verified.
    - `person` EnforcerV3InternalDomainPersonPerson
      - `created_at` string
      - `dob` string
      - `email_verified` boolean
      - `first_name` string
      - `home_address_line1` string
      - `home_address_line2` string
      - `home_city` string
      - `home_country` string
      - `home_place_id` string: Home address — canonical, owned here (cybrid KYC + report-builder read it). HomePlaceID references an enforcer-geo place; the rest is a denormalized snapshot so /auth/me serves the address without a geo round-trip.
      - `home_postal_code` string
      - `home_region` string
      - `id` string
      - `last_name` string
      - `name` string: deprecated display name; prefer first_name/last_name
      - `phone_verified` boolean
      - `primary_email` string
      - `primary_phone` string
      - `profile_image_url` string
      - `updated_at` string
    - `pin_length` integer
    - `pin_set` boolean: PinSet / PinLength describe the caller's app-PIN enrollment (never the PIN material itself). No omitempty on pin_set: false must serialize so clients distinguish "no pin" from "older server". pin_length (4 or 6) lets the client render the right number of entry boxes.
    - `privy_user_id` string
    - `profile_completed` boolean
    - `referral` InternalDeliveryHttpHandlerMeReferral
      - `code` string
      - `id` string
    - `role` EnforcerV3InternalDomainRoleRole
      - `admin_managed` boolean
      - `created_at` string
      - `cross_tenant` boolean: Capability grants (additive; all false = baseline-only, like "user").
      - `description` string
      - `id` string
      - `mpp_ops` boolean: MPPOps: read-only MPP deposit-intent operator views (migration 000129).
      - `name` string
      - `slug` string
      - `tenant_manage` boolean
      - `tenant_read` boolean
      - `tenant_write` boolean
      - `updated_at` string
    - `tenant` EnforcerV3InternalDomainTenantTenant
      - `account_abstraction` boolean
      - `allow_user_discovery` boolean: AllowUserDiscovery gates contact discovery for the whole tenant: when true, accounts may be discoverable (and new accounts default to discoverable); when false, discovery is off tenant-wide regardless of account.is_discoverable.
      - `app_url` string
      - `auth_provider` string: AuthProvider / WalletProvider: nil = inherit the instance default.
      - `banner_url` string
      - `billing_status` string: BillingStatus is the tenant's platform-billing dunning state, PROJECTED from enforcer-billing (migration 000087): "" | active | past_due | restricted | suspended | collections. enforcer-billing is the source of truth; this is a copy the billing.status_changed consumer keeps current so the value rides Accessor.billing_status on every resolve with no hop. "" means UNKNOWN and readers MUST treat it as active (fail open). `<-:false` makes GORM never write these two columns: Repository.Update is a whole-row Save, so without it an admin editing the tenant's name from a stale struct would silently roll the projection back. Only BillingStatusStore.ApplyBillingStatus writes them, by targeted UPDATE under the monotonic entered_at guard.
      - `billing_status_entered_at` string
      - `code` string
      - `code_expires_at` string: CodeExpiresAt is when the join code stops working everywhere a code is accepted, signing in and joining alike, as if it had been rotated away. Absent: the code never expires. Set by POST /tenants/{id}/code.
      - `created_at` string
      - `description` string
      - `id` string
      - `logo_url` string
      - `metadata` EnforcerV3InternalDomainTenantJSONBMap
      - `metered_sends_enabled` boolean: MeteredSendsEnabled turns on per-transaction limit metering: every value leg leaving the platform is measured against the caller's live cap before the server co-signs. Meaningful only while Web3Enabled is true — the pair (false, true) is refused on write; see ValidateWeb3Gates.
      - `modules` string[]: Modules is the tenant's MODULE entitlements as group slugs, prefixed `module_` (migration 000094, V3-02). Which HALO-Lego surfaces this tenant has bought — NOT which of them any given account may use, which is the role's question. It lives on the tenant rather than in account_groups because it is a fact about the TENANT: a module per account would mean auto-joining every new account and would make "remove one person from a module the tenant paid for" expressible. The resolver UNIONS this into Accessor.GroupSlugs, so every leaf's RequireModule reads it with no new field and no extra hop. Same `<-:false` guard and the same targeted-write rule as BillingStatus above: Repository.Update is a whole-row Save, so without it an admin editing the tenant's name from a stale struct would silently revoke every module. Only ModuleStore writes these two columns.
      - `modules_updated_at` string
      - `name` string
      - `payment_link_url` string
      - `privy_app_id` string
      - `self_join_policy` 'open' | 'invite_only'
      - `status` 'active' | 'inactive' | 'suspended'
      - `theme_colors` EnforcerV3InternalDomainTenantJSONBMap
      - `thumbnail_url` string
      - `updated_at` string
      - `wallet_provider` string
      - `web3_enabled` boolean: Web3Enabled is the tenant-wide web3 kill switch. False rejects EVERY web3 transaction for the tenant — guarded sends, signing, and RPC writes alike — so it is deliberately a gate rather than an enforcement mode. Defaults true (migration 000066) because false would disable web3 for every existing tenant on deploy.
    - `terms` InternalDeliveryHttpHandlerMeTerms
      - `has_mandatory_pending` boolean
      - `items` InternalDeliveryHttpHandlerMeTermsItem[]
        - `accepted` boolean
        - `accepted_at` string
        - `id` string
        - `is_mandatory` boolean
        - `title` string
        - `type` string
        - `version` string
    - `updated_at` string
    - `username` string
    - `verifications` EnforcerV3InternalAuthzVerificationRef[]
      - `external_customer_id` string
      - `provider` string
      - `status` string
      - `type` string: kyc | kyb
    - `wallet_address` string: WalletAddress is the account's PRIMARY wallet — the denormalized mirror of account_wallets' primary row, which is what deposits, contact lookups and every account-keyed read resolve to. Absent when the account holds none. Deliberately just the address, and deliberately taken from the account row so /auth/me stays a single query: this is the hydration path, and it is called on every app open. The wallet's KIND and its signing posture are a different question with a different cost — GET /me/wallets lists all of them with kind and owner quorum, and GET /me/wallets/multisig reads the live posture from the provider. A client that needs to know whether the primary is a co-signed 2-of-2 must ask one of those; this field only says which address is primary.
    - `wallet_send_migration_required` boolean: WalletSendMigrationRequired is true when this account holds a smart wallet on a send-enforced tenant (a tenant_connections row carries the flag) that hasn't yet had the mandatory app-key owner quorum attached. Set by the Me handler (needs a connections lookup, so NewMeData can't compute it). The PWA reads it here to prompt the one-time, user-authorized owner-change. No omitempty: a false must serialize so clients distinguish "not required" from "older server".
  - `success` boolean

## Other responses

- `400`: Bad Request
- `401`: Unauthorized

---

[API](https://skmtc.dev/instruxi/apis/enforcer-api.md) · [All operations](https://skmtc.dev/instruxi/apis/enforcer-api/llms.txt) · [OpenAPI document](https://skmtc.dev/instruxi/apis/enforcer-api/revisions/fa82bc1becc0?raw)
