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:
| Field | Type | Notes |
|---|---|---|
id | string | Public id, prefixed acc_. |
email | string | Login email. |
name | string | null | Display name; null falls back to email in the UI. |
tier | enum | One of the seven tier slugs. |
status | enum | active / suspended / deleted. |
timezone | string | null | IANA name (Europe/Amsterdam); null means UTC fallback for client renders. |
slug | string | null | V-298a — readable handle (lowercase a-z + 0-9 + hyphen, 3-32 chars). Null when unset. |
region | enum | null | V-298b — stated infrastructure-region preference (us / eu / apac). Informational for v1; routing is governed by DPA Annex 3. |
avatar_url | string | null | V-352b — short-lived (1h) presigned R2 GET URL. Null when no avatar uploaded or the public bucket isn’t wired in this deploy. |
mfa_enrolled | boolean | True once TOTP enrolment is verified. |
concurrent_session_cap | number | Per-tier active-session ceiling. |
concurrent_session_active | number | Currently-active count. |
profile_cap | number | null | Per-tier profile ceiling; null on enterprise (custom). |
profile_count | number | Currently-saved profiles. |
teams | array | V-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. Returns409 Conflictwhen another account already owns the slug. Null clears.region— V-298b. One ofus/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-log — client.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.