HELIX 3 Docs
Helix Phone

Permissions & consent

The 12 Phone permission scopes, how apps declare them, how players grant them, and the audit trail behind every grant.

Every sensitive Phone SDK call is gated by a permission scope. An app can only use a scope if it (1) declared it in its manifest and (2) the player granted it. Calls without permission return empty/null rather than throwing — so your app degrades gracefully.

The scopes

Prop

Type

How granting works

Declare the scopes in your manifest's permissions array. Requesting a scope you didn't declare is an error.

Default grants. account.basic is always granted. Built-in apps get all their declared scopes by default. Third-party apps start with only account.basic — everything else needs the player's explicit consent.

Request at point of use. Ask for a scope when the feature is first used, and check the result:

const granted = await phone.permissions.request("media.pick");
if (!granted.includes("media.pick")) {
  phone.ui.toast("Media access is needed to attach a photo.");
  return;
}

Players manage grants in Settings → the app's permission list, and can revoke any scope except account.basic at any time.

Checking permissions

await phone.permissions.list();              // → granted scopes for this app
await phone.permissions.has("wallet.read");  // → boolean

Because ungranted calls return empty values, you can also just try and handle the empty result — but checking first lets you show better UI.

messages.send_with_consent is deliberately named: sending messages on a player's behalf is sensitive, so every send is audited and the player can revoke the scope to immediately cut an app off. Build messaging features assuming the player is watching who they message.

The audit trail

Every permission-relevant event is recorded — grants, revocations, denied calls, and calls to undeclared scopes:

type PhonePermissionAudit = {
  appId: string;
  scope: PhonePermissionScope;
  outcome:
    | "granted" | "denied" | "undeclared"
    | "consent_granted" | "consent_revoked" | "consent_denied";
  reason: string | null;
  createdAt: string;
};

Players can review this in Settings; it's also what powers abuse investigations. For developers, the takeaway is simple: declare only what you use, and request it in context — over-asking shows up in the audit log and erodes trust (and store standing).

On this page