D driftstack docs

Account

/v1/account/me is the calling account’s self-edit surface. The endpoint is bearer-authenticated; it never honours the team-RBAC X-Driftstack-Account header — /me always operates on the caller’s own account, even when the caller has admin role on a team owner’s account.

Get the calling account

GET /v1/account/me

const me = await client.account.me();

Returns the account’s full self-visible state:

FieldTypeNotes
idstringPublic id, prefixed acc_.
emailstringLogin email.
namestring | nullDisplay name; null falls back to email in the UI.
tierenumOne of the seven tier slugs.
statusenumactive / suspended / deleted.
timezonestring | nullIANA name (Europe/Amsterdam); null means UTC fallback for client renders.
slugstring | nullV-298a — readable handle (lowercase a-z + 0-9 + hyphen, 3-32 chars). Null when unset.
regionenum | nullV-298b — stated infrastructure-region preference (us / eu / apac). Informational for v1; routing is governed by DPA Annex 3.
avatar_urlstring | nullV-352b — short-lived (1h) presigned R2 GET URL. Null when no avatar uploaded or the public bucket isn’t wired in this deploy.
mfa_enrolledbooleanTrue once TOTP enrolment is verified.
concurrent_session_capnumberPer-tier active-session ceiling.
concurrent_session_activenumberCurrently-active count.
profile_capnumber | nullPer-tier profile ceiling; null on enterprise (custom).
profile_countnumberCurrently-saved profiles.
teamsarrayV-326c — owner accounts the caller is a member of. Each entry: owner_account_id, role, membership_id. Empty array when not on any team.

Update the calling account

PATCH /v1/account/me

Partial update — pass any subset of name, timezone, slug, region. At least one field must be present. Pass null to clear a nullable field.

{
  "name": "Acme B.V.",
  "timezone": "Europe/Amsterdam",
  "slug": "acme",
  "region": "eu"
}
  • name — 1-120 trimmed chars; null clears (UI falls back to email).
  • timezone — IANA name (Europe/Amsterdam); null clears (UTC fallback).
  • slug — 3-32 chars, lowercase a-z + 0-9 + hyphen, no leading or trailing hyphen, no consecutive hyphens. Returns 409 Conflict when another account already owns the slug. Null clears.
  • region — V-298b. One of us / eu / apac. Null clears.

Returns the same shape as GET /v1/account/me with the new values applied.

Avatar upload

POST /v1/account/me/avatar — V-352b.

Inline base64 body. The image lands in the EU-jurisdiction Cloudflare R2 bucket; the response includes a presigned read URL. Field shape:

{
  "data_base64": "iVBORw0KGgoAAAANSUhEUgAAA...",
  "content_type": "image/png"
}
  • Allowed content_type: image/png, image/jpeg, image/webp.
  • Max raw size: 2 MiB (route body limit is 3.5 MiB to allow the base64 envelope).
  • Returns { avatar_url, content_type, bytes }.

DELETE /v1/account/me/avatar clears the avatar pointer; the R2 object is left in place (a sweeper job collects orphaned keys off the hot path).

Active sign-ins (V-355)

The dashboard’s “active sign-ins” panel and SDK client.account resource expose the calling account’s web-session list:

const { data } = await client.account.listWebSessions();
for (const session of data) {
  console.log(session.os, session.browser, session.last_used_at, session.current);
}
sessions = client.account.list_web_sessions()
for s in sessions["data"]:
    print(s["os"], s["browser"], s["last_used_at"], s["current"])
list, _ := client.Account.ListWebSessions(ctx)
for _, s := range list.Data {
    fmt.Println(s.OS, s.Browser, s.LastUsedAt, s.Current)
}

The entry with current: true is the calling session itself. IP addresses are deliberately omitted; user-agents are reduced to OS + browser bucket per V-211 anonymity. Revoke individual sessions with revokeWebSession(id) / revoke_web_session(id) / RevokeWebSession(ctx, id). Revoke every other session in one call with revokeAllOtherWebSessions() / equivalent.

Effective rate limits (V-258)

Read the calling account’s effective per-bucket rate-limit config including any active overrides:

const cfg = await client.account.rateLimits();
for (const bucket of cfg.buckets) {
  console.log(bucket.bucket_key, bucket.capacity, bucket.refill_per_second, bucket.source);
}

source is tier_default for unbounded tier-derived caps or override when staff has applied a per-account adjustment; override_expires_at is non-null in the override case.

Email preferences (V-204)

Per-event opt-out toggles for non-critical customer emails:

const prefs = await client.emailPreferences.list();
await client.emailPreferences.optOut('billing-renewal-reminder');

Critical emails (verification, password-reset, billing-failure, subscription-cancellation, support-ack) are not opt-outable — they’re absent from the OptOutableEmailEvent enum on purpose.

Audit log

Programmatic access to the customer audit log lives at /api/audit-logclient.auditLog.list() / client.auditLog.iterate() walk the same V-216 ledger the dashboard renders. Pair with the V-216 export endpoint for GDPR Article 20 portability bulk-pulls.

MFA enrollment + step-up

MFA management is on client.mfa.* (V-353b) — status, enroll, verify, disable, regenerateRecoveryCodes. The login-time MFA exchange + step-up are on client.auth.*mfaChallenge (V-353d) + mfaStepUp (V-353e). Full walkthrough at /api/auth#mfa-challenge-v-353d.

Why /me ignores team-RBAC

The X-Driftstack-Account header (V-326e) routes most /v1/* requests to a team owner’s account. /v1/account/me is the exception: editing a team owner’s display name, slug, region, or avatar via a member’s bearer token would be surprising. If team-scoped account editing is needed in the future, it lands as a separate /v1/team/owners/:id/... surface with explicit semantics — not an opt-in flag on /me.