Developers Cengel
/developers/oauth/user-info
Live Sign In Start

OAuth2 · User Info

User Info

Understand the UserInfo endpoint response structure and scope-based data filtering.

Response shape

UserInfo returns the signed-in user’s profile as JSON. Optional fields appear only when the matching scope was granted — check for a key before reading it.

Endpoint: GET /api/v2/oauth/userinfo with Authorization: Bearer <access_token>.

Base response (always)

These fields return for any valid access token (even with only openid). They are not gated on the profile scope.

{
  "id": "1234567890123456789",
  "user": {
    "displayName": "Duru",
    "username": "duru",
    "avatar": {
      "baseUrl": "//media.cengel.net",
      "path": "/avatars/1234567890123456789/latest",
      "url": "//media.cengel.net/avatars/1234567890123456789/latest",
      "color": "#000"
    },
    "about": "User bio or description"
  },
  "language": "en",
  "createdAt": "2024-01-01T00:00:00Z"
}
Prefer user.avatar.url when displaying an image. baseUrl + path are for composing your own CDN URLs.

With profile scope

Adds theme preference at the top level:

{
  "theme": "system"
}

With email scope

{
  "user": {
    "email": {
      "username": "john",
      "domain": "example.com",
      "isVerified": true
    }
  }
}

With details scope

{
  "user": {
    "website": "https://example.com",
    "location": "Istanbul, Turkey",
    "birthDate": "1990-01-01"
  }
}

birthDate is null when the user keeps birth privacy non-public.

With phone / social

{
  "user": {
    "phone": "+905551234567",
    "socialLinks": [
      { "platform": "x", "url": "https://x.com/cengelstudio", "label": "X" },
      { "platform": "github", "url": "https://github.com/cengelstudio", "label": "GitHub" }
    ]
  }
}

With space.read scope

Returned only for managed (Space) accounts. Otherwise the space key is omitted. If the user has no membership, value is null.

{
  "user": {
    "space": {
      "id": "9876543210987654321",
      "displayName": "Acme Corp",
      "isOwner": true,
      "domain": "acme.com"
    }
  }
}

Field reference

Quick lookup for types and when each field appears.

Always Base fields

Field Type Notes
id string Snowflake user id
user.displayName string Display name
user.username string Unique username
user.avatar.url string Ready-to-use CDN URL
user.avatar.baseUrl string e.g. //media.cengel.net
user.avatar.path string Path under the media host
user.avatar.color string UI theme preference when set
user.about string May be empty string
language string May be null
createdAt string ISO 8601, or empty string

profile Theme

FieldTypeNotes
theme string light, dark, system, or null

email Email

FieldTypeNotes
user.email.username string Local part before @
user.email.domain string Domain after @
user.email.isVerified boolean Verification flag

details / phone / social

FieldTypeNotes
user.website string Nullable
user.location string Nullable
user.birthDate string YYYY-MM-DD or null (privacy)
user.phone string E.164 or null
user.socialLinks array Objects with platform, url, label; may be []

space.read Space

FieldTypeNotes
user.space object null if no membership; omitted if not a managed account
user.space.id string Space snowflake id
user.space.displayName string Space display name
user.space.isOwner boolean true when this user owns the space
user.space.domain string Primary domain (e.g. acme.com)

Scope-based filtering

UserInfo returns only data for scopes the user granted. Missing scope → missing fields (not empty placeholders), except base fields which always appear.

Practice: Treat optional keys as optional in your client types. Do not assume email or space exist.

Authentication

Send a valid access token on every UserInfo call.

GET /api/v2/oauth/userinfo
Authorization: Bearer YOUR_ACCESS_TOKEN
  • Missing/invalid token → 401 Unauthorized
  • User revoked access → 403 (“Access revoked by user”)
  • Banned/suspended account → 403 Forbidden
  • Responses are always JSON