Product: ExtremeX ONE Identity, Consent & Application Data Platform Publisher: ExtremeX Technology Document classification: Developer reference / implementation proposal Revision: 1.0 — 23 October 2026 Existing SDK baseline: 3.2.0 (as shown on the reviewed integration guide) Status: Existing API inventory plus proposed additions; proposed methods are not confirmed shipped. Primary documentation: https://client.extremextechnology.com/docs Account portal: https://one.extremextechnology.com/account
1. Executive summary
ExtremeX ONE provides federated authentication, granular consent, privacy-preserving identity attributes, per-application encrypted storage and unified account-management flows. This specification standardises the one facade, documents existing behaviour, and proposes new application metadata, legal-document consent, organisation-membership and account-lifecycle interfaces.
Design objectives:
- Privacy by default: Applications receive only fields explicitly granted through ONE; raw date of birth and exact age must never be exposed.
- Least privilege: Account, application, organisation and moderation operations are authorised independently and enforced on the ONE server.
- Predictable outcomes:
nullmeans an unavailable/withheld profile value, never an assumed negative result or transport failure. - Cross-platform parity: Shared logical contracts map to Kotlin, Android, Swift, JavaScript, React Native, Flutter, Java and other supported clients.
- Strong consistency of consent: Consent changes and disconnects take effect at the protected resource boundary; stale local caches cannot override server policy.
- Developer experience: Stable method names, typed results, explicit errors, completion prompts, diagnostics and migration guidance.
1.1 Implementation status legend
| Label | Meaning |
|---|---|
| Existing — documented | Found in the reviewed live SDK 3.2 integration guide. |
| Proposed | Requested API or expanded contract; requires backend/SDK development and release verification. |
| Recommended | Design guidance or optional future enhancement, not a guarantee of current implementation. |
Compatibility note: The current native guide uses one.auth.signOut() for local sign-out. one.auth.data.signOut() below is a proposed forwarding alias, not a replacement for the existing call. The identifiers Varified and Enroled are preserved where explicitly requested for source compatibility; correctly spelled aliases may be introduced later.
2. Existing integration architecture
Host application (Android / iOS / Web / Desktop / Server)
|
+--> ONE SDK facade (`one`)
|
+--> Auth: browser login, OAuth 2.0 / PKCE, callback, tokens
+--> Profile: consent-filtered identity & derived age checks
+--> App data: app/user-scoped typed key-value operations
+--> [PROPOSED] App metadata and legal-doc consent
+--> [PROPOSED] Organisation access and moderation gateways
|
+--> ExtremeX ONE OAuth / API infrastructure
|
+--> Identity & verified attributes
+--> Live consent and revocation enforcement
+--> Encrypted application data
+--> [PROPOSED] Versioned legal document records
+--> [PROPOSED] Organisation/project role bindings
+--> [PROPOSED] Privileged moderation service2.1 Authentication sequence
- Register an application in ExtremeX Client and obtain
APP_ID,CLIENT_ID, and the documented redirect configuration. - Configure requested data fields in Client → Data access, with a specific purpose and required/optional classification.
- Start sign-in using the supported ONE browser-based flow. Public clients use OAuth 2.0 authorization code + PKCE (S256); never embed client secrets in shipped clients.
- ONE authenticates the user and displays its hosted consent and missing-profile completion screens.
- Validate callback parameters and exchange the authorization code using the documented platform flow.
- Bind an authenticated session to the
oneSDK instance. Refresh and persist rotating refresh tokens using platform-secure storage when available and authorised. - Invoke protected profile, application-data or proposed APIs. Each server call evaluates current access rights.
- On local sign-out, clear local session state. Revocation of granted access is a separate ONE account action unless a future endpoint explicitly implements it.
Current service endpoints (documented):
Authorization: https://one.extremextechnology.com/oauth/authorize
API base: https://api.extremextechnology.com/one
GET /oauth/authorize
POST /oauth/token
GET /oauth/userinfo
GET /data?key=KEY
PUT /data
DELETE /data?key=KEYImportant: The proposed app-document, organisation, and moderation endpoints later in this specification are design proposals. Their paths and payloads must be implemented and published before SDK methods can call them.
3. Shared API conventions
3.1 Namespaces
| Facade | Responsibility |
|---|---|
one.auth.data.* | Consent-filtered user profile and derived identity attributes. |
one.auth.* | Authentication lifecycle and session controls. |
one.app.data.* | Registered app's public metadata and legal-document consent (proposed). |
one.data.* | Per-user, per-app encrypted key-value storage. |
one.org.* | Recommended future separation for organisation administration; existing requested aliases may remain under one.auth.data. |
one.admin.* | Recommended privileged moderation namespace; setBan can remain as a compatibility delegate after authorisation. |
3.2 Result and null semantics
A getter may return T | null only when the requested profile attribute is missing, incomplete, or not granted. A missing but granted field should be eligible for ONE's existing completion experience; a denied field must not trigger a coercive completion prompt.
| Outcome | Return/behaviour | Required UI handling |
|---|---|---|
| Present and permitted | T | Use the value for the approved purpose. |
| Known condition is false | false | Treat as verified evaluation of the boolean predicate, not lack of data. |
| Missing profile field, consent granted | null | Offer ONE's existing completion prompt where supported, then retry after user returns. |
| Field not granted / permission withdrawn | null for profile getter | Explain feature limitation; offer voluntary permissions review, not automatic escalation. |
| Invalid/expired login | Explicit auth error | Refresh once where valid, otherwise request sign-in. |
| Revoked/disconnected app | Explicit access-revoked error | Discard session and stop protected operations. |
| Network failure | Explicit network error | Show retry action; do not convert to null or false. |
| Malformed request / server policy denial | Explicit validation/access error | Correct request or route to authorised administrator. |
Boolean?/boolean | null has three meaningful states: true, false, and null. Never treat null as false for age gating, verification, employment, membership, or eligibility.
3.3 Recommended type contracts
The following TypeScript-style contracts describe desired cross-platform semantics; they do not imply a production TypeScript SDK export.
type Nullable<T> = T | null;
type CountryCode = string; // Prefer ISO 3166-1 alpha-2, or configured legacy OTHER
type LanguageCode = string; // Define ISO language-code policy in backend schema
type UserName = [firstName: string | null, lastName: string | null];
type PhoneParts = [countryCode: string, nationalNumber: string];
type AddressParts = [
premises: string | null,
street: string | null,
locality: string | null,
region: string | null,
country: string | null,
postalCode: string | null
];
type Bitmap = PlatformNativeBitmap | DecodedRgbaBitmap;
interface DecodedRgbaBitmap { width: number; height: number; pixels: Uint8Array; }Why phone numbers use strings: Country calling codes and subscriber numbers are identifiers, not quantities. Integers remove leading zeroes, cannot preserve +, extensions or significant formatting, and can overflow numeric representations. The live documentation already returns [country_code, phone_number] as string arrays. Preserve the exact storage and normalisation rules documented by ONE.
Why arrays preserve null slots: A six-part address must retain positional meaning even when apartment or county is absent. Do not shift components to fill gaps. For new API versions, a named object is preferable to positional arrays, while the requested existing array API remains supported.
3.4 Input validation principles
APP_ID: registered application identifier, not an arbitrary client-supplied namespace.CLIENT_ID: public OAuth client identifier; always resolve it server-side to the authenticated registration.CUSTOM_URI_SCHEME: registered callback scheme; cannot authorise revocation or disconnection by itself.ORG_IDandPROJECT_ACCESS_ID: opaque identifiers, validated server-side against authorised relationships.UUIDC: opaque identity handle scoped to a defined audience; never use an unverified client-supplied value as proof of identity.- Keys: predefined variable names where declarations are required; validate type, size, nesting and ownership.
- Duration: strongly typed units; no ambiguous numbers without a duration unit.
4. Existing functions — user profile
All functions in this section are existing — documented in the reviewed native integration guide, subject to SDK/platform availability and live consent. The odd historical spelling of Varified is retained to match the exposed API.
4.1 Identity, avatar and name
| Function | Permission | Return | Behaviour |
|---|---|---|---|
one.auth.data.getAvatar() | avatar | Platform bitmap or null | Returns a decoded ONE profile image; documented max 256 × 256 pixels. |
one.auth.data.getName() | first_name, last_name | [firstName, lastName] or null | Returns fixed-position name components; missing individual components remain null. |
one.auth.data.getEmail() | email | string | null | Returns permitted personal email. |
one.auth.data.getPhoneNumber() | phone | [countryCode, nationalNumber] | null | String parts without coercion to integers. |
one.auth.data.getAddress() | address | AddressParts | null | Six positions: premises, street, city, region, country, postal code. |
one.auth.data.getCountry() | address | string | null | Country derived from saved home address; currently uses country code or OTHER. |
one.auth.data.getNationality() | nationality | string | null | Nationality as permitted by scope and saved profile. |
one.auth.data.getCountryOfBirth() | country_of_birth | string | null | Country of birth only; not exact birth date. |
one.auth.data.getLanguages() | languages | string[] | null | User's saved language entries. |
Bitmap platform mapping (documented): Android Bitmap; iOS UIImage; macOS NSImage; browser ImageBitmap; Java AvatarBitmap; source-based runtimes may expose RGBA pixels and dimensions. Always adapt images using the platform UI layer; do not treat bitmaps as arbitrary file uploads.
4.2 Privacy-safe age and birthday checks
| Function | Permission | Return | Precise meaning |
|---|---|---|---|
one.auth.data.isUserTeen() | date_of_birth | boolean | null | User is 13+. This does not mean aged 13–17 only. |
one.auth.data.isUserLegal() | date_of_birth | boolean | null | User is 18+. This is an age-threshold result, not a legal capacity guarantee in every jurisdiction. |
one.auth.data.isUserAdult() | date_of_birth | boolean | null | User is 25+. The name is legacy; do not assume 18–24-year-olds are minors. |
one.auth.data.isUserAgeVarified() | date_of_birth | boolean | null | ONE's identity-backed age-verification procedure passed. A self-entered DOB alone never marks this true. |
one.auth.data.isUserBirthday() | date_of_birth | boolean | null | Calendar day matches user's birthday; current implementation uses UTC. |
Hard privacy invariant: No client profile helper, REST userinfo response, log, debug payload, analytics property, SDK cache, or exception detail may expose exact date of birth or exact age. The currently reviewed integration guide expressly describes threshold-only results; historical SDK versions must not be relied on to access raw DOB.
Example — verified 18+ gate (illustrative Kotlin):
// Execute on a worker/coroutine after successful sign-in.
val is18Plus: Boolean? = one.auth.data.isUserLegal()
val hasVerifiedAge: Boolean? = one.auth.data.isUserAgeVarified()
when {
is18Plus == null || hasVerifiedAge == null -> showUnavailableAgeState()
is18Plus && hasVerifiedAge -> openRestrictedFeature()
else -> showEligibilityOrVerificationScreen()
}The UI functions above are application-defined placeholders. isUserAgeVarified() may offer ONE's existing age-verification action on a known false result, depending on the platform/UI integration.
4.3 Contact verification
| Function | Permission | Return | Behaviour |
|---|---|---|---|
one.auth.data.isUserEmailVarified() | email | boolean | null | true when saved personal email is verified. |
one.auth.data.isPhoneNumberVerified() | phone | boolean | null | true when saved personal phone number is verified. |
one.auth.data.isWorkEmailVarified() | work_email | boolean | null | true when saved work email is verified. |
one.auth.data.isWorkPhoneNumberVerified() | work_phone | boolean | null | true when saved work phone is verified. |
An explicitly present unverified contact attribute returns false; an absent or withheld attribute returns null. A true verification state confirms ONE's verification mechanism, not perpetual ownership or suitability for any regulated communication.
4.4 Work profile
| Function | Permission | Return | Behaviour |
|---|---|---|---|
one.auth.data.getWorkEmail() | work_email | string | null | Permitted work email. |
one.auth.data.getWorkPhoneNumber() | work_phone | [countryCode, nationalNumber] | null | Work number as string components. |
one.auth.data.getWorkAddress() | work_address | AddressParts | null | Six-position work location. |
one.auth.data.getWorkCountry() | work_address | string | null | Country from work address. |
4.5 Missing-data and verification prompts
The existing ONE UI already supports missing-profile completion for granted fields, and supported platforms expose missing-data handlers. The account completion page is provided by ONE: host applications must not collect ONE account passwords, impersonate the hosted consent flow, or bypass denial.
App requests a field
|
+-- Not granted -----------------> null; no missing-data prompt
|
+-- Granted but missing ---------> null + optional ONE completion prompt
| |
| +--> User updates ONE profile
| |
| +--> Refresh profile / retry
|
+-- Granted and present ---------> value
|
+-- Session, server, network failure -> explicit errorFor identity verification, ONE may offer a verification prompt linking to the account portal. The documented process includes trusted-ID and liveness/face-match checks; do not implement a fake local verification toggle.
5. Existing functions — encrypted application data
Status: Existing — documented (one.data.set/get/rep/del). Each entry belongs to the signed-in user and the registered application namespace and requires the applicable App data consent (attributes).
| Function | Return concept | Behaviour |
|---|---|---|
one.data.set("VARIABLE_NAME", VALUE) | Success / error | Create or overwrite an application variable. |
one.data.get("VARIABLE_NAME") | JSON-compatible value or null | Read an existing value; a missing key resolves to null. |
one.data.rep("VARIABLE_NAME", VALUE) | Success / error | Replace/upsert a value; a non-null replacement is required by the native guide. |
one.data.del("VARIABLE_NAME") | Success / error | Remove one variable from this user's application namespace. |
The currently reviewed integration guide also describes list and remove aliases in some supported SDK contexts. Do not assume that a JSON null stored value can always be distinguished from a missing key using get alone; prefer explicit existence metadata if that distinction is required.
5.1 Storage guarantees and limits (documented)
- Encryption at rest: AES-256-GCM, with application-derived keys.
- Compression: DEFLATE before encryption.
- Accepted content: JSON-compatible strings, numbers, booleans,
null, arrays and objects. - Maximum uncompressed value: 16 KiB of UTF-8 JSON.
- Maximum write batch: 100 keys.
- Maximum nesting: 20 levels.
- Maximum request body: 1.7 MB.
- App isolation: one application cannot read another application's namespace.
- File uploads, media blobs, data URLs, binary byte arrays and disguised files are not supported in app data.
App-variable definitions and type checking remain server-owned. set() or rep() cannot evade declaration constraints or grant boundaries.
5.2 Example (documented API shape; illustrative Kotlin)
// Execute after sign-in and outside the Android main thread.
one.data.set("theme", "dark")
val theme = one.data.get("theme") // "dark" or null
one.data.rep("theme", "light")
one.data.del("theme")Implementation recommendation: Treat set and rep as idempotent upserts for the same key/value, but document actual concurrency and conditional-write semantics before promising optimistic locking. Do not assume cross-request transactions or atomic increments.
6. Proposed functions — registered application metadata
Status: Proposed. Public branding already appears in the documented SDK login view; the following explicit accessor API is not verified as released.
| Function | Suggested type | Contract |
|---|---|---|
one.app.data.appDisplayName() | string | null | Registered display name, normalised for safe presentation. |
one.app.data.appDescription() | string | null | Current public description of the calling application. |
one.app.data.appWebsite() | string | null | Verified/public HTTPS website URL, if configured. |
one.app.data.appIcon() | Bitmap | null | Fetched/decoded public app icon, platform-native representation. |
6.1 Behaviour
- The SDK resolves its authenticated/registered APP_ID rather than accepting a caller-provided identity override.
- The server returns only public metadata explicitly configured by the application owner.
- Metadata may be cached with a short TTL and invalidated on app-settings changes; do not claim instant cross-device cache coherency.
- Missing metadata returns
null; network errors still raise typed errors. - Applications must not use display name, description, icon or website as proof of a legal entity or security authority.
- The SDK should sanitise URLs and text before rendering. Load remote artwork only from approved HTTPS locations with bounded bytes, dimensions and redirects.
Suggested metadata response:
{
"app_id": "com.example.product",
"display_name": "Example Product",
"description": "A privacy-aware example application.",
"website": "https://example.com",
"icon_url": "https://example.com/icon.png",
"updated_at": "2026-10-23T00:00:00Z"
}The above is a proposed payload, not an existing ONE endpoint response.
7. Proposed functions — versioned legal documents and user agreements
Status: Proposed. Design these APIs as a distinct document-consent service, not as regular application data. Consent to terms is separate from granting personal-data scopes.
7.1 Exact requested API
one.app.data.docs.isAgreed(type, slug, version)
one.app.data.docs.get(type, slug, version)
one.app.data.docs.agreeTo(type, slug, version)7.2 Document types and versioning
enum docType {
TERMS = "TERMS",
POLICY = "POLICY",
LICENCE = "LICENCE",
OTHER = "OTHER"
}
type DocumentVersion = string; // "1.0", "1.01", "1.001" (exact identifiers)Do not represent versions as floating point. 1.0, 1.00, 1.01, and 1.001 may be different publication identifiers, while floating-point conversion can erase trailing zeroes or introduce rounding. Normalise and validate versions server-side using an agreed string format; avoid numeric comparison of document revisions.
7.3 Operations
| Function | Suggested return | Contract |
|---|---|---|
docs.get(type, slug, version) | LegalDocument | null | Retrieve an immutable published version. A missing document may return null; unauthorised/private docs return explicit access errors. |
docs.isAgreed(type, slug, version) | boolean | null | true if the authenticated user has an active, verifiable acceptance of this exact version; false if definitively not accepted; null if the identity/acceptance state is unavailable. |
docs.agreeTo(type, slug, version) | AgreementReceipt | Explicitly record an authenticated user's informed acceptance of an exact published document. |
7.4 Recommended document and receipt models
interface LegalDocument {
appId: string;
type: docType;
slug: string;
version: DocumentVersion;
title: string;
locale: string;
contentFormat: "markdown" | "text" | "html";
content: string;
sha256: string; // Digest of canonical published bytes
effectiveAt: string; // ISO 8601
publishedAt: string; // ISO 8601
status: "published" | "superseded" | "withdrawn";
}
interface AgreementReceipt {
receiptId: string;
appId: string;
documentType: docType;
documentSlug: string;
documentVersion: DocumentVersion;
documentSha256: string;
acceptedAt: string; // Server timestamp, ISO 8601
subject: string; // Opaque user subject, not raw personal identity
consentMethod: "one_hosted" | "verified_app_flow";
locale: string;
}7.5 Agreement lifecycle
- Application owner creates and publishes a document version using an authenticated administrative publishing workflow.
- ONE stores the immutable publication and content digest; an edited draft must be published as a new version.
- SDK retrieves the exact version and presents its complete contents or a trusted rendering.
- The user performs an explicit affirmative action, e.g. selecting Agree and continue. Passive page views or prechecked boxes must not count as consent.
- Server verifies identity, publisher, app ownership, document state and digest before issuing the receipt.
isAgreedchecks the stored receipt for that specific(app_id, type, slug, version, digest)tuple.- Later document versions do not silently inherit older acceptance. Define a re-acceptance policy explicitly, with jurisdiction-specific legal review if needed.
- For privacy policy acknowledgements, do not incorrectly equate contractual acknowledgement with GDPR consent for processing. Track the legal basis separately.
7.6 Example (pseudocode, not a currently shipped SDK call)
const type = docType.TERMS;
const slug = "app-terms";
const version = "1.001";
const doc = await one.app.data.docs.get(type, slug, version);
if (!doc) throw new Error("Requested terms document is unavailable");
renderLegalDocument(doc); // Must be fully accessible and readable.
// Invoke ONLY after the user intentionally accepts the displayed document:
async function onUserPressedAgree() {
const receipt = await one.app.data.docs.agreeTo(type, slug, version);
await persistReceiptReference(receipt.receiptId); // Optional app-owned reference
}Proposed backend endpoints (not currently documented):
GET /apps/me/metadata
GET /apps/me/documents/{type}/{slug}/versions/{version}
GET /apps/me/documents/{type}/{slug}/versions/{version}/agreement
POST /apps/me/documents/{type}/{slug}/versions/{version}/agreementNever send untrusted raw HTML directly to a WebView without sanitisation and strict script/content security controls.
8. Proposed functions — extended user profile
| Function | Suggested return | Contract and access policy |
|---|---|---|
one.auth.data.UUIDC() | string | null | Unique opaque user identification code; prefer app- or organisation-scoped pseudonymous identity rather than a globally correlatable identifier. |
one.auth.data.userGender() | string | null | Self-described optional gender attribute; return only when explicitly requested and granted. Do not infer it. |
one.auth.data.getLanguages.primary() | string | null | User-selected primary language from their saved preferences, subject to languages scope. |
one.auth.data.isOrgEnroled(ORG_ID, PROJECT_ACCESS_ID, UUIDC) | boolean | null | Check active organisation and specified project enrollment through the server; do not trust the supplied UUIDC alone. |
one.auth.data.getOrgInfo(ORG_ID) | PublicOrgInfo | null | Read an explicitly public, published organisation profile. |
one.auth.data.signOut() | Completion / error | Proposed alias for documented one.auth.signOut(); clears the current local session only. |
one.auth.data.disconnectProfile(CLIENT_ID, APP_ID, CUSTOM_URI_SCHEME) | DisconnectResult | Proposed account-connection revocation, distinct from local sign-out; requires strong server-side verification and user confirmation. |
8.1 UUIDC() — user identity safety
Problem: A globally stable identifier shared across unrelated applications enables unwanted cross-app tracking. Current ONE identity tooling already documents an opaque stable sub for its authentication flow.
Recommended: Make UUIDC an opaque pairwise identifier derived from the authenticated subject and authorised audience (e.g. app or organisation), or explicitly document where it matches an existing sub. It must not contain an email, phone, DOB, national ID or reversible personal-data component.
Rules:
- A client cannot ask the server to impersonate an arbitrary UUIDC.
- The server determines the currently signed-in user from the token/session.
- Org access checks must compare the supplied handle with that authenticated identity, or require privileged delegated access.
- Reject enumeration, excessive queries and cross-tenant lookups.
- Never log UUIDC in third-party analytics as a cross-service tracking key.
8.2 userGender()
Gender can be sensitive personal data in context. Make collection optional, purpose-specific and expressly grantable. Recommended values include a user-specified string/controlled vocabulary with a privacy-preserving prefer not to say state. Do not infer gender from name, photos or identity-document markers. Avoid mixing null (not granted/missing) with a deliberately selected privacy preference.
8.3 getLanguages.primary()
The requested syntax uses .primary() as a submethod, which means getLanguages is a callable object as well as a function in some languages. Java/Kotlin cannot implement that ergonomically without an intermediate facade. Recommended cross-platform alias:
Requested compatibility syntax: one.auth.data.getLanguages.primary()
Recommended standard syntax: one.auth.data.getPrimaryLanguage()If the requested syntax is essential, implement a language-specific getLanguages facade; otherwise prefer getPrimaryLanguage() to avoid a brittle or impossible cross-platform binding. The primary language must be selected explicitly or according to a declared fallback policy, never guessed silently from nationality.
9. Proposed functions — organisation membership and public organisations
9.1 Membership eligibility
Exact requested signature:
one.auth.data.isOrgEnroled(ORG_ID, PROJECT_ACCESS_ID, UUIDC)Recommended policy:
- Verify that
ORG_IDexists and the project belongs to it. - Resolve authenticated user identity server-side; ensure the UUIDC argument is the same user unless caller has a dedicated administrative permission.
- Evaluate current organisation membership, membership status, project binding, expiry and suspension.
- Return
trueonly if all required checks pass at the time of evaluation. - Return
falsefor a definitive non-membership or absent project entitlement that the caller is authorised to check. - Return
nullonly for unavailable/withheld identity context; reject unauthorised third-party enumeration with explicit errors. - Enforce all protected project actions again on the backend. A UI-level
trueis not an access token.
Potential improved interface:
interface MembershipAssessment {
enrolled: boolean;
orgId: string;
projectAccessId: string;
effectivePermissions?: string[];
expiresAt?: string | null;
checkedAt: string;
}A richer response should require appropriate scope and must not reveal private organisation membership to arbitrary applications.
9.2 Organisation public information
one.auth.data.getOrgInfo(ORG_ID)Recommended response:
interface PublicOrgInfo {
id: string;
name: string;
displayName?: string;
description?: string;
website?: string;
logoUrl?: string;
verified?: boolean;
publicProjects?: Array<{ id: string; title: string }>;
updatedAt: string;
}Only administrator-approved public fields may be returned. Never expose employee rosters, internal project IDs, ownership information, billing configuration or unlisted membership from a public organisation lookup.
10. Proposed functions — moderation and bans
10.1 Requested function
one.auth.data.setBan(duration, date, reason)Security classification: Privileged administrative mutation. This method must not be callable by every signed-in app/user. Its existence under auth.data should be a compatibility shim; the preferred long-term interface is a server-side one.admin.users.setBan(...) API with delegated moderation scope, tenant separation and audit logging.
10.2 Duration and permanence
You requested a permanent ban when the duration in minutes exceeds 100 days.
100 days = 100 × 24 × 60 = 144,000 minutes
if durationMinutes > 144000:
banKind = PERMANENT
else:
banKind = TEMPORARYBoundary: Exactly 144000 minutes remains temporary under your stated greater-than condition. If you intend 100 days or longer, use >= and update the contract.
10.3 Recommended validated ban command
type BanKind = "TEMPORARY" | "PERMANENT";
interface BanRequest {
targetSubject: string; // Required: authenticated/admin-resolved user identifier
appId: string; // App-scoped by default
durationMinutes: number; // Integer >= 1
effectiveAt?: string; // ISO 8601; omission means server now
reasonCode: string; // Stable policy category
reason: string; // Moderation explanation
evidenceReference?: string; // Internal controlled reference, not raw evidence
idempotencyKey: string;
}
interface BanRecord {
id: string;
kind: BanKind;
effectiveAt: string;
expiresAt: string | null;
reason: string;
createdBy: string;
createdAt: string;
}Signature problem to resolve: setBan(duration, date, reason) omits the target user. Under the proposed alias, it can only target the currently authenticated user, which is generally unsuitable for moderation. Do not allow the calling application to mutate arbitrary accounts without an explicit, server-verified subject and scope. A more useful privileged method is setBan(targetSubject, durationMinutes, effectiveAt, reason).
10.4 Enforcement requirements
- Require administrator service authentication, a moderation role and app/tenant-scoped authorisation.
- Default to app-scoped bans, not global ONE account bans. Platform-wide enforcement is a separate high-trust workflow.
- Verify policy, reason, target, duration and effective date server-side.
- Store server-computed expiry; clients cannot decide they are unbanned by changing device clocks.
- Apply denial to protected ONE-facing resources for the relevant app according to its moderation policy.
- Record immutable audit evidence: actor, action, reason code, timestamp, target scope and previous/new state.
- Provide appeal/review and unban administrative processes, with retention according to applicable policy.
- Rate-limit requests, protect against abusive moderators, and avoid disclosing sensitive moderation evidence in public API errors.
Existing baseline: The reviewed guide describes app-scoped bans administered from the Client Users tab and exposed moderation error details. It does not establish that one.auth.data.setBan() already exists.
11. Proposed account lifecycle — sign-out and disconnect
11.1 Sign-out
Proposed alias: one.auth.data.signOut()
Current API: one.auth.signOut()Current documented behaviour: clear the local app's ONE session. It does not guarantee sign-out from the system browser, revoke refresh tokens remotely or remove the application's user-data grant.
Required client handling: Clear local tokens, personally identifying view models, cached sensitive display state and active protected requests. If asynchronous sign-out is supported, complete those steps before reporting success.
11.2 Disconnect the connected profile
one.auth.data.disconnectProfile(CLIENT_ID, APP_ID, CUSTOM_URI_SCHEME)Status: Proposed. This is a materially stronger operation than sign-out and needs a backend revocation contract.
Proposed sequence:
- Require a live, authenticated user session and a deliberate action to disconnect this app.
- Resolve
CLIENT_IDandAPP_IDfrom server-side registration; reject any mismatch with current caller identity. - Validate the callback scheme only for navigation, never as proof of consent or ownership.
- Present ONE-hosted confirmation specifying which app will lose access and what happens to app-specific stored data.
- Revoke the appropriate application's grants and refresh-token chain on the server; invalidate access at protected endpoints.
- Clear SDK local session/tokens, unsubscribe push or websocket streams where applicable and purge sensitive caches.
- Return a non-sensitive receipt/status. Data deletion or retention must follow a separate explicitly documented retention policy; disconnection must not be silently treated as irreversible deletion without notice.
Recommended return:
interface DisconnectResult {
appId: string;
disconnected: boolean;
effectiveAt: string;
receiptId: string;
dataDisposition: "retained_per_policy" | "deletion_requested" | "deleted";
}Important: A disconnected token must not be able to call the same method to obtain new access. This is an account-connection lifecycle workflow, not an OAuth callback trick.
12. Permissions and data-minimisation matrix
This matrix identifies baseline documented profile scopes and proposed extensions. Registration, consent-screen field names and backend enforcement must be updated together.
| Data or operation | Scope / gate | Null on missing/denied? | Completion prompt? | Notes |
|---|---|---|---|---|
| Name | first_name, last_name | Yes | If granted but missing | Fixed positions. |
| Avatar | avatar | Yes | If granted but missing | Decoded bitmap. |
| Email / email verified | email | Yes | If granted but missing | Verification predicate returns false for known unverified. |
| Personal phone / verified | phone | Yes | If granted but missing | String components. |
| Home address / country | address | Yes | If granted but missing | Same scope, country derived. |
| DOB-derived age and birthday flags | date_of_birth | Yes | If granted but missing | Never expose raw DOB. |
| Work email / verified | work_email | Yes | If granted but missing | Distinct from personal scope. |
| Work phone / verified | work_phone | Yes | If granted but missing | Distinct from personal scope. |
| Work address / country | work_address | Yes | If granted but missing | Country derived. |
| Nationality | nationality | Yes | If granted but missing | Distinct from country of birth. |
| Country of birth | country_of_birth | Yes | If granted but missing | Never exact DOB. |
| Languages | languages | Yes | If granted but missing | Primary language requires a defined profile field (proposed). |
| Gender | gender (proposed) | Yes | If granted but missing | Optional and purpose-specific. |
| App data | attributes / App data | Key missing returns null | No profile prompt for missing key | Requires app variable declarations, where configured. |
| Long-lived session | offline | N/A | OAuth user decision | Refresh tokens rotate when used. |
| Public app metadata | Registration/visibility policy (proposed) | Yes | No | No account profile permission needed for public branding. |
| Legal-document retrieval | Published/visibility policy (proposed) | Not found → null | No | Receipt lookup requires authenticated subject. |
| Agreement receipt write | Authenticated user action (proposed) | No | User intentionally accepts | Not interchangeable with data-processing consent. |
| Organisation membership | org.membership.read (proposed) | Only for unavailable own identity | No | No third-party enrollment enumeration. |
| Organisation info | Public record (proposed) | Not found → null | No | Public-only attributes. |
| Ban assignment | moderation.ban.write (proposed) | No | No | Privileged backend-only gate. |
| Disconnect profile | User-authenticated revocation (proposed) | No | ONE-hosted confirmation | Server-side revocation. |
Registration rule: Request only the scope needed for a working user feature, clearly disclose the use, and allow optional fields to be withheld without blocking unrelated functionality.
13. Error model and recovery
Use the existing ExOneException style or platform-equivalent exceptions for transport/auth/validation failures. Extend it with stable machine-readable codes rather than parsing error-message text.
| Code (existing or proposed) | Meaning | Suggested handling |
|---|---|---|
invalid_grant | Invalid/used authorization code or refresh grant | Restart login; never replay consumed code. |
session_expired | No refreshable session | Return to ONE sign-in. |
access_revoked | User disconnected or scope revoked | Clear sensitive local state. |
insufficient_scope | Application lacks required grant | Explain limitation; route to standard permission process. |
network_error | Transport unavailable | Retry with bounded backoff and cancellation. |
invalid_argument (proposed) | Invalid identifier, duration, version or input | Correct developer call; do not retry blindly. |
document_not_published (proposed) | Requested legal version unavailable for acceptance | Display unavailability; do not record agreement. |
agreement_conflict (proposed) | Version/digest mismatched or superseded | Fetch and display current published version. |
org_access_denied (proposed) | Caller cannot inspect membership | Show access denied; do not leak membership. |
moderation_forbidden (proposed) | Caller lacks administrative privileges | Disable admin control; audit attempt. |
ban_active (proposed code) | Access blocked by policy | Show permitted reason/expiry and appeal path. |
disconnect_requires_confirmation (proposed) | No validated user confirmation | Open ONE-hosted confirmation. |
HTTP baseline: The reviewed API reference associates 400 with invalid input, 401 with invalid session, 403 with missing/revoked access, 413 with oversized requests and 415 with non-JSON content.
Operational requirements: Never convert 401, 403, timeout, rate limiting, backend policy errors or corruption to null. Avoid infinite sign-in loops; apply bounded retries and idempotency keys for sensitive mutations.
14. Cross-platform implementation patterns
14.1 Android / Kotlin
The reviewed native guide shows SDK 3.2.0 and use of ExOneAndroid. The current working Kotlin/Android install pattern is:
// settings.gradle.kts
maven { url = uri("https://client.extremextechnology.com/repo") }
// app/build.gradle.kts — inside dependencies { ... }
implementation("com.extremex.one:exone-android:3.2.0")import com.extremex.one.android.ExOneAndroid
val one = ExOneAndroid(applicationContext, "APP_ID", "CLIENT_ID")
// Show ONE's native sign-in component and handle the callback through
// the documented Activity lifecycle. Never call protected APIs before auth.Native callbacks and sign-in wiring depend on the host Activity. Register APP_ID://callback appropriately in the Android manifest and handle both initial and subsequent callback Intents. The official guide documents attaching the client to recreated activities where necessary. Blocking profile/data reads belong on worker threads; async callbacks touching UI must switch to the main thread.
14.2 Kotlin Multiplatform
Keep domain policy and nullable result interpretation in commonMain, but delegate identity/image representations and browser/callback handling to platform-specific adapters. Do not assume Java Bitmap is available in common Kotlin code. Use an immutable shared decoded-image structure or expect/actual platform return types.
// Conceptual shared interface — not a shipped SDK declaration.
interface OneProfileReader {
suspend fun is18Plus(): Boolean?
suspend fun isAgeVerified(): Boolean?
suspend fun getNameParts(): List<String?>?
}14.3 Web / Next.js
Use the official browser sign-in mechanism and an authenticated server/session model. Keep token exchange, any confidential credentials, privileged moderation credentials and organisation administration on the backend. Avoid putting broad bearer tokens or personal profile payloads in publicly rendered HTML or client logs.
// Conceptual call flow; docs/agreement methods require implementation.
async function loadFeatureEligibility(one: OneClient) {
const legal = await one.auth.data.isUserLegal();
const verified = await one.auth.data.isUserAgeVarified();
if (legal === null || verified === null) return "unknown";
return legal && verified ? "eligible" : "ineligible";
}OneClient above is an illustrative application type; adapt to the actual JS SDK export and callback/promise form in your installed version.
14.4 iOS / Swift
Use ASWebAuthenticationSession for sign-in and register the callback URI scheme/universal link described by ONE. Convert optional NSNumber/bool results carefully so nil remains unknown. Present age-completion and legal-agreement UIs in accessible sheets; do not infer identity from device settings.
14.5 React Native / Flutter / desktop
Use the ONE-supported source module or native bridge for the target platform, an external system browser for OAuth and a callback handler that validates state and PKCE. Images must use the platform-appropriate decoded representation. For headless applications, forward missing-data completion links to an authenticated UI rather than opening a server-side browser.
Feature-parity rule: A method should not be presented as universally supported merely because the TypeScript/Kotlin signature appears in this specification. Track builds and actual runtime tests platform by platform.
15. Worked integration scenarios
15.1 Profile display with progressive completion
- Authenticate with ONE.
- Request
first_name,last_name,avatarand any optional contact scopes with stated reasons. - Call
getName()andgetAvatar()after sign-in. - If a granted profile field is missing, offer the existing ONE completion route; do not demand access to an ungranted optional field.
- Retry on return, after current consent is confirmed.
- Render partial information safely (e.g. first name only) rather than failing the whole screen.
15.2 Age-restricted experience
isUserLegal() == true AND isUserAgeVarified() == true
-> eligible for a verified-18+ feature (subject to other requirements)
One false, other known
-> not yet eligible under this rule; offer ONE verification where appropriate
Either null
-> eligibility unknown; keep restricted functionality inaccessibleAge threshold alone must never substitute for any independently required regional age-assurance, licensing or regulatory requirement.
15.3 Persisting user settings
one.data.set("appearance", "system")
val appearance = one.data.get("appearance")
one.data.rep("appearance", "dark")Use platform-local storage for device-only or offline-only settings where server synchronisation is unnecessary. Do not store passwords, OAuth client secrets or oversized files in app data.
15.4 App terms re-acceptance (proposed)
- Product team publishes
TERMS/app-terms/1.001immutably. - App fetches document and verifies success.
- It checks the user's receipt for exactly
1.001. - If
false, display the full new text and explain differences where relevant. - Only the user's active confirmation invokes
agreeTo. - Backend records a verifiable receipt; app proceeds.
- If
nullor network failure, do not assert acceptance. Apply an explicit, legally reviewed offline policy.
15.5 Organisation-gated project access (proposed)
- Authenticated user enters an org-bound feature.
- Client requests an eligibility check for the current user and the exact project.
- ONE verifies org enrollment and project membership server-side.
- Client may show eligible UI on
true. - Every subsequent protected operation rechecks authorisation server-side; a cached boolean does not confer access.
15.6 Administrative ban (proposed)
- Authenticated and authorised moderator selects a user and an app scope.
- Server validates the request, including target identity, duration, reason and moderation privileges.
- More than 144000 minutes selects
PERMANENT; otherwise compute server-side expiry. - Server records a policy-bound ban and audit trail; middleware blocks affected operations.
- User sees the permitted reason/expiry and appeal route. Client is never trusted to implement the restriction alone.
16. Suggested SDK facade evolution
To preserve existing calls while keeping authority boundaries clear:
one
├── auth
│ ├── signOut() [EXISTING]
│ └── data
│ ├── getAvatar() [EXISTING]
│ ├── getName() [EXISTING]
│ ├── isUserTeen() / isUserLegal() [EXISTING]
│ ├── isUserAdult() [EXISTING]
│ ├── isUserAgeVarified() [EXISTING]
│ ├── isUserBirthday() [EXISTING]
│ ├── contact, address, language... [EXISTING]
│ ├── UUIDC() [PROPOSED]
│ ├── userGender() [PROPOSED]
│ ├── getPrimaryLanguage() [RECOMMENDED]
│ ├── getLanguages.primary() [REQUESTED; platform adapter]
│ ├── isOrgEnroled(...) [PROPOSED]
│ ├── getOrgInfo(...) [PROPOSED]
│ ├── signOut() [PROPOSED alias]
│ ├── disconnectProfile(...) [PROPOSED]
│ └── setBan(...) [PROPOSED privileged alias]
├── app
│ └── data
│ ├── appDisplayName() [PROPOSED]
│ ├── appDescription() [PROPOSED]
│ ├── appWebsite() [PROPOSED]
│ ├── appIcon() [PROPOSED]
│ └── docs
│ ├── get(type,slug,version) [PROPOSED]
│ ├── isAgreed(type,slug,version) [PROPOSED]
│ └── agreeTo(type,slug,version) [PROPOSED]
└── data
├── set(key,value) [EXISTING]
├── get(key) [EXISTING]
├── rep(key,value) [EXISTING]
└── del(key) [EXISTING]Recommended follow-on: Formalise a separate one.admin and one.org namespace in a future major release without abruptly removing legacy aliases.
17. Security, privacy and compliance engineering
17.1 Mandatory security properties
- Require TLS and verify remote hosts and certificate trust.
- Use OAuth PKCE on public clients; never bundle
client_secretin APK, IPA, desktop binaries or JavaScript. - Validate OAuth
state, code lifetime, redirect URI and PKCE verifier; do not replay authorization codes. - Store and rotate refresh tokens securely; replace the persisted token after each successful refresh.
- Revalidate scope, user identity, organisation membership and revocation server-side for protected operations.
- Treat callback URI schemes as routing identifiers, not authorisation credentials.
- Avoid logging profile data, DOB-derived internal values, tokens, full addresses, contact information and legal-agreement evidence in telemetry.
- Apply rate limits and audit logging to privileged actions and agreement receipts.
- Isolate per-user/per-app storage namespaces.
- Prevent unsafe HTML/script execution in document rendering.
- Provide accessible consent and missing-data completion experiences.
17.2 Privacy controls
- Date-of-birth minimisation: never return exact DOB or age from this API.
- Purpose limitation: request and retain only fields necessary for features.
- User choice: distinguish missing data from denied consent, and avoid repeated prompts after denial.
- Revocation: ensure withdrawn permission blocks future protected reads even if cached data exists locally.
- Sensitive demographics: gender and nationality require appropriately justified access; no derived demographic inference.
- Identity correlation: pairwise/tenant-scoped UUIDC over global tracking IDs whenever possible.
- Document auditability: associate acceptance with immutable document digest, version and server time.
- Legal caveat: GDPR and other applicable laws can impose additional obligations; legal-document consent, data-processing consent and age verification are separate concepts.
17.3 Threat model checkpoints
| Threat | Control |
|---|---|
| Malicious app probes denied profile field | Server-enforced live consent; field omitted/null. |
| Compromised client submits someone else's UUIDC | Bind identity to session; reject forged targets. |
Ordinary app submits setBan | Backend moderator-only permission gate. |
| App falsely marks a user age verified | ONE-owned identity verification state only. |
| Tampered terms content during acceptance | Immutable publication + digest and receipt validation. |
| Redirect scheme spoofing | PKCE, state, exact registered callback and external browser. |
| Refresh-token reuse | Rotation and revocation checks. |
| Stale offline membership entitlement | Recheck all protected actions on backend. |
| Untrusted document markup | Sanitised rendering, script disabled, content policy. |
| Network failures hidden as null | Typed errors and transparent UI recovery. |
18. Verification and quality-assurance matrix
| Test ID | Test case | Expected outcome |
|---|---|---|
| AUTH-01 | Normal browser sign-in | Validated callback creates session. |
| AUTH-02 | Modified state / PKCE mismatch | Authentication denied; no session. |
| AUTH-03 | Reuse consumed code | Rejected. |
| AUTH-04 | Rotated refresh token reused | Rejected, secure session recovery. |
| PROFILE-01 | Optional field denied | Relevant getter returns null, no coercive completion prompt. |
| PROFILE-02 | Granted field missing | null and ONE completion flow where supported. |
| PROFILE-03 | Value present but unverified | Relevant verification returns false. |
| PROFILE-04 | Exact DOB requested via SDK/REST | Never exposed. |
| PROFILE-05 | Age 12 / 13 / 17 / 18 / 24 / 25 | Correct 13+/18+/25+ threshold matrix. |
| PROFILE-06 | Birthday near UTC midnight | Behaviour matches documented UTC calendar. |
| PROFILE-07 | Phone with leading zeroes | Strings preserve all digits. |
| PROFILE-08 | Sparse address | Six slots preserved with null positions. |
| PROFILE-09 | Missing avatar / huge source image | null or safely bounded bitmap, no resource exhaustion. |
| DATA-01 | Set/get/replace/delete | Round-trip and deletion correct. |
| DATA-02 | Cross-user/app read | Denied or isolated. |
| DATA-03 | Invalid declared variable type | Explicit validation error. |
| DATA-04 | Oversized value/file-like payload | Correct rejection and no partial corrupt write. |
| DOC-01 | Exact 1.0 vs 1.001 | Different receipt keys. |
| DOC-02 | Accept without user action | Rejected. |
| DOC-03 | Tamper document after publish | Immutable digest check rejects mutation. |
| DOC-04 | Re-accept current version | Idempotent outcome with consistent receipt. |
| ORG-01 | Authenticated user is enrolled in org but not project | false. |
| ORG-02 | Client spoofs a different UUIDC | Denied without leaking membership. |
| ORG-03 | Revoked organisation membership | Protected operation denied. |
| BAN-01 | Duration = 144000 minutes | TEMPORARY under > rule. |
| BAN-02 | Duration = 144001 minutes | PERMANENT. |
| BAN-03 | Ordinary user invokes ban API | Forbidden; audited. |
| DISC-01 | Local signOut | Clears local session; consent not silently revoked. |
| DISC-02 | Confirmed disconnect | Grants revoked; protected calls fail; data retention policy respected. |
| NET-01 | Offline or timeout | Explicit network error, never eligibility false. |
Release verification: Run unit tests, integration tests, consent-revocation tests, security reviews and real-device testing on each supported SDK platform. Do not mark platform parity complete based only on shared source code compiling.
19. Delivery plan and acceptance criteria
Phase 1 — Baseline consistency
- Freeze a canonical inventory of
3.2.0exported SDK symbols per platform. - Document null/error handling and keep DOB exposure prohibited at every layer.
- Align existing helpers and return types; retain spelling aliases where already shipped.
- Add shared contract tests for age thresholds, personal/work scope enforcement and app-data operations.
Phase 2 — Public app metadata
- Implement secure public metadata read, safe icon rendering and cache invalidation.
- Add platform-specific decoded image adapters and accessibility fallbacks.
- Document precise availability and installation versions.
Phase 3 — Immutable legal documents
- Implement document publication, exact string versions, content digests and per-user acceptance receipts.
- Provide audit export, supersession rules and failure recovery.
- Validate intentional user acceptance in each UI platform.
Phase 4 — Extended profile and organisation APIs
- Define
genderand selected-primary-language storage/permissions. - Define UUIDC identity scope and org/project access checks.
- Implement anti-enumeration checks, organisation public profiles, membership cache policies and server-side enforcement.
Phase 5 — Moderation and disconnection
- Design explicit privileged moderation principals, scopes, reason codes, review and audit workflows.
- Implement disconnect confirmation and grant/token revocation.
- Keep local sign-out and remote disconnect semantically distinct.
Phase 6 — Publication and observability
- Publish versioned API reference, migration guides, SDK generated examples and release notes.
- Measure success/error/latency by operation without collecting personal content in logs.
- Complete security/privacy reviews and real-device/platform certification checks.
Definition of done
A proposed method is considered shipped only when all are true: backend contract exists; access policy is enforced; SDK implementation is available in a published version; platform-specific types are validated; test cases pass; errors are documented; the main developer documentation is updated; and a migration/release note identifies its availability.
20. Developer quick reference
// Existing user profile
one.auth.data.getAvatar()
one.auth.data.getName()
one.auth.data.isUserTeen() // 13+
one.auth.data.isUserLegal() // 18+
one.auth.data.isUserAdult() // 25+
one.auth.data.isUserAgeVarified() // ONE identity-verified age
one.auth.data.isUserBirthday() // current UTC birthday match
one.auth.data.isUserEmailVarified()
one.auth.data.isPhoneNumberVerified()
one.auth.data.getEmail()
one.auth.data.getPhoneNumber() // [callingCode, number] strings
one.auth.data.getAddress() // 6-part address
one.auth.data.getCountry()
one.auth.data.getWorkEmail()
one.auth.data.getWorkPhoneNumber()
one.auth.data.isWorkEmailVarified()
one.auth.data.isWorkPhoneNumberVerified()
one.auth.data.getNationality()
one.auth.data.getWorkAddress()
one.auth.data.getWorkCountry()
one.auth.data.getCountryOfBirth()
one.auth.data.getLanguages()
// Existing app data
one.data.set("KEY", VALUE)
one.data.get("KEY")
one.data.rep("KEY", NON_NULL_VALUE)
one.data.del("KEY")
// Existing session method
one.auth.signOut()
// Proposed public app info
one.app.data.appDisplayName()
one.app.data.appDescription()
one.app.data.appWebsite()
one.app.data.appIcon()
// Proposed legal-doc consent
one.app.data.docs.get(docType.TERMS, "app-terms", "1.001")
one.app.data.docs.isAgreed(docType.TERMS, "app-terms", "1.001")
one.app.data.docs.agreeTo(docType.TERMS, "app-terms", "1.001")
// Proposed profile and org methods
one.auth.data.UUIDC()
one.auth.data.userGender()
one.auth.data.getLanguages.primary() // requested; binding design needed
one.auth.data.getPrimaryLanguage() // recommended cross-platform alias
one.auth.data.isOrgEnroled(ORG_ID, PROJECT_ACCESS_ID, UUIDC)
one.auth.data.getOrgInfo(ORG_ID)
// Proposed account lifecycle and privileged moderation
one.auth.data.signOut() // forwarding alias
one.auth.data.disconnectProfile(CLIENT_ID, APP_ID, CUSTOM_URI_SCHEME)
one.auth.data.setBan(durationMinutes, date, reason) // privileged only; signature needs target resolution21. Canonical references
- ExtremeX ONE Client Integration Guide: https://client.extremextechnology.com/docs — reviewed 23 October 2026; baseline SDK 3.2.0 and current behaviour for native profile helpers, age checks, app data, login/callback and missing-data prompts.
- ExtremeX ONE account: https://one.extremextechnology.com/account — profile completion and age-verification destination referenced in the live guide.
- OAuth 2.0 for Native Apps: RFC 8252 — https://www.rfc-editor.org/rfc/rfc8252
- Proof Key for Code Exchange (PKCE): RFC 7636 — https://www.rfc-editor.org/rfc/rfc7636
Final contract notice
This document is an advanced integration specification and implementation handoff, not a claim that all proposed APIs already exist. Sections explicitly tagged Existing — documented reflect behaviour reviewed from the public documentation. Sections tagged Proposed or Recommended are design requirements for future ExtremeX ONE SDK and backend releases. Production consumers must use only methods confirmed by their installed SDK/API version.
