HELIX 3 Docs
Helix Phone

Phone SDK reference

Every Helix Phone SDK namespace and method — runtime, account, permissions, storage, notifications, media, camera, wallet, payments, contacts, messages, calls, presence, social, feeds, ui, and lifecycle.

This is the complete surface of the Helix Phone SDK as it ships today (v0.1.0). Each method lists its required permission scope; calls without a granted scope return null/[] rather than throwing. Methods marked mock are wired against a mocked backend until the real integration lands — see Proposed & in-progress.

Creating the SDK

import { createHelixPhoneSdk } from "@helix/phone-sdk";

const phone = createHelixPhoneSdk({
  appId: "studio.example",   // required — your manifest appId
  accessToken?: string,       // first-party/server only; omit for sandboxed apps
  manifest?: PhoneAppManifest,
  bridgeNonce?: string,       // usually read from ?helixBridgeNonce automatically
});

createHelixPhoneSdk returns an object with the namespaces below. It auto-selects the bridge or direct transport.


runtime

MethodReturnsNotes
runtime.bootstrap()PhoneBootstrapItem | nullAccount, installed apps, permissions, launch context, runtime config.
runtime.manifest()PhoneAppManifest | nullThis app's manifest.
runtime.launchContext()PhoneAppLaunchContext | nullHow the app was opened (web / notification / shell).

account

MethodReturnsScope
account.getCurrentUser()PhoneAccount | nullaccount.basic (always granted)
const me = await phone.account.getCurrentUser();
// { userId, username, displayName, avatarUrl, phoneNumber, bio }

permissions

MethodReturnsNotes
permissions.list()PhonePermissionScope[]Scopes currently granted to this app.
permissions.has(scope)booleanCheck a single scope.
permissions.request(scopes)PhonePermissionScope[]Request one scope or an array; returns granted scopes. Throws if a scope wasn't declared in the manifest.

storage

Per-app key–value storage. Keys match ^[a-zA-Z0-9._:-]{1,128}$; values are JSON up to 32 KB. Writes are atomic upserts. Scope: storage.app.

MethodReturns
storage.get<T>(key)T | null
storage.set<T>(key, value)PhoneAppStorageRecord<T> | null
storage.delete(key)void
await phone.storage.set("draft", { caption: "gm", assetId });
const draft = await phone.storage.get<{ caption: string; assetId: string }>("draft");

notifications

Create and manage this app's notifications. Push is rate-limited to ~12/hour per app. Scope: notifications.push.

MethodReturnsNotes
notifications.list()PhoneNotification[]This app's notifications.
notifications.push({ title, body, deepLink? })PhoneNotification | nulltitle ≤120, body ≤500. deepLink opens the app at a path.
notifications.markRead(notificationId)PhoneNotification | null
notifications.markAppRead(appId?)voidMarks all of this app's notifications read.
await phone.notifications.push({
  title: "Render ready",
  body: "Your postcard finished exporting.",
  deepLink: "studio.example/exports",
});

media

The player's album (photos/videos). Upload accepts a File. Scope: media.pick.

MethodReturnsNotes
media.listAlbum()AlbumAsset[]All album assets, newest first.
media.pickFromAlbum()AlbumAsset | nullThe most recent asset (the picker UI returns the chosen one).
media.upload(file, { caption?, worldName? })AlbumAsset | nullUpload an image/video into the album.
const photo = await phone.media.pickFromAlbum();
if (photo) attach(photo);

camera

Capture into the album. mockmockCapture currently produces a placeholder asset (or copies sourceAssetId); real world/device capture is proposed. Scope: camera.capture.

MethodReturns
camera.mockCapture({ sourceAssetId?, caption?, worldName?, type? })AlbumAsset | null

wallet

Read-only balances. Scope: wallet.read.

MethodReturns
wallet.getBalance()PhoneWalletBalance | null
const w = await phone.wallet.getBalance();
// w.lix.totalBalance, w.coins.balance — read-only

payments

In-app purchases priced in LIX, from the app's manifest iap catalog. Purchases are idempotent. mock — entitlements are granted but LIX isn't debited yet (see In-app purchases). Scope: payments.

MethodReturnsNotes
payments.getProducts()PhoneIapProduct[]The app's product catalog.
payments.purchase(sku, { idempotencyKey? })PhoneIapEntitlement | nullPass an idempotencyKey so retries can't double-charge.
payments.getEntitlements()PhoneIapEntitlement[]What the player owns.
payments.restore()PhoneIapEntitlement[]Re-fetch entitlements (e.g. on a new device).
const ent = await phone.payments.purchase("demo_theme_pack", {
  idempotencyKey: crypto.randomUUID(),
});

contacts

The player's contacts, backed by the Helix social graph. Scope: social.connections.read.

MethodReturns
contacts.list()PhoneAccount[]

messages

Threads and sending, consent-gated. Scope: messages.send_with_consent.

MethodReturnsNotes
messages.threads()MessageThread[]Threads with unread counts.
messages.send({ participantId, body, attachment? })Message | nullbody ≤2000; attachment is an AlbumAsset.
messages.markRead(threadId)MessageThread | null

calls

Voice calls and call logs. mock — calls are simulated until the voice API lands. Scope: voice.calls.

MethodReturns
calls.logs()CallLog[]
calls.mockCall(contactId)CallLog | null
calls.mockIncoming(contactId)CallLog | null
calls.updateStatus(callId, status, durationSeconds?)CallLog | null

presence

The player's current world/instance. mock — returns a fixed world until live presence lands. Scope: presence.world.

MethodReturns
presence.getCurrentWorld()PhonePresenceWorld | null

social

Opt-in discovery profiles and swiping (the dating/meeting primitive). Swipe matching is mock. Scope: social.discovery.

MethodReturnsNotes
social.getMyDiscoveryProfile()PhoneSocialDiscoveryProfile | null
social.updateDiscoveryProfile({ displayName?, bio?, avatarUrl?, active? })PhoneSocialDiscoveryProfile | nullCreate/update.
social.disableDiscoveryProfile()voidSets the profile inactive.
social.getDiscoveryFeed({ limit? })PhoneSocialDiscoveryProfile[]Opted-in candidates.
social.recordSwipe(candidateUserId, "like" | "pass")PhoneSocialDiscoverySwipeResult | null

feeds: helixgram & h

Because Helixgram (photo/video) and H (short posts) are first-party, the SDK exposes them directly. Both require social.discovery; Helixgram createPost/createStory also require media.pick.

phone.helixgram

MethodReturns
helixgram.feed(page?) / helixgram.followingFeed(page?)HelixgramPost[]
helixgram.userPosts(userId, page?)HelixgramPost[]
helixgram.stories(page?) / helixgram.userStories(userId, page?)HelixgramStory[]
helixgram.userSocial(userId)HelixgramProfile | null
helixgram.searchUsers(query, limit?)HelixgramProfile[]
helixgram.follow(userId) / helixgram.unfollow(userId)result
helixgram.createPost({ asset, caption, location })HelixgramPost | null
helixgram.createStory({ asset, caption, location })HelixgramStory | null
helixgram.like(postId) / helixgram.unlike(postId)result
helixgram.comment(postId, body) / helixgram.deleteComment(postId, commentId)result
helixgram.deletePost(postId) / helixgram.deleteStory(storyId)void
helixgram.report({ subjectType, subjectId, reason, details? })result

page is { limit?, before? } for cursor pagination.

phone.h

MethodReturns
h.feed(page?) / h.followingFeed(page?)HPost[]
h.trends({ limit? })HTrend[]
h.userPosts(userId, page?)HPost[]
h.replies(postId, page?)HPost[]
h.userSocial(userId)HProfile | null
h.createPost(body) / h.reply(postId, body)HPost | null
h.repost(postId) / h.like(postId) / h.unlike(postId)result
h.follow(userId) / h.unfollow(userId)result
h.report({ subjectType, subjectId, reason, details? })result

ui

Native phone UI affordances. No scope required (UI-only).

MethodReturnsNotes
ui.toast(body)voidTransient toast.
ui.confirm({ title, body?, confirmLabel?, cancelLabel? })booleanNative confirm dialog.
ui.shareSheet({ items })PhoneUiShareResultOS share sheet; items are { title?, text?, url? }.
ui.setBadge(count)voidSet this app's home-screen badge count.

lifecycle

MethodReturnsNotes
lifecycle.on(event, handler)() => voidSubscribe; returns an unsubscribe fn. Events: foreground / suspend / resume / close.
lifecycle.emit(event, snapshot?)voidMostly internal.

See Runtime & the bridge for the lifecycle model.


Comprehensive & kept in sync

This page covers the entire shipping SDK surface. A drift-detection pipeline compares it against lib/phone/sdk.ts on the helix3 branch and flags any namespace or method that lands or changes, so the reference can't silently fall behind the code.

On this page